2017-02-01 8 views
1

Ich habe eine Datenbank mit 3 Tabellen, MasterInfo, PrimDescT, SecDescT.Anfrage Optimierung für innere Joins in sqlite

CREATE TABLE masterInfo (id INTEGER PRIMARY KEY AUTOINCREMENT, 
primDescId INTEGER, 
secDescId INTEGER, 
category INTEGER, 
UNIQUE(primDescId, secDescId, category)); 

CREATE TABLE primDescT (id INTEGER PRIMARY KEY, 
primDesc nvarchar(512)); 

CREATE TABLE secDescT (id INTEGER PRIMARY KEY, 
secDesc nvarchar(512)); 

INSERT INTO primDescT VALUES(1,'XXXX'); 
INSERT INTO primDescT VALUES(2,'YYYY'); 
INSERT INTO primDescT VALUES(3,'ZZZZ'); 
INSERT INTO primDescT VALUES(4,'SSSS'); 

INSERT INTO secDescT VALUES(1,'AAA'); 
INSERT INTO secDescT VALUES(2,'BBB'); 
INSERT INTO secDescT VALUES(3,'CCC'); 

INSERT INTO masterInfo VALUES(1,1,1,1); 
INSERT INTO masterInfo VALUES(2,2,2,2); 
INSERT INTO masterInfo VALUES(3,3,1,1); 
INSERT INTO masterInfo VALUES(4,4,3,2); 

Tabellen hat masterInfo 1.765.137 Zeilen, 312210 Zeilen in primDescT, 105458 Zeilen in secDescT.

Ich habe die folgende Abfrage verwendet, um die Ergebnisse abzurufen.

SELECT m.id AS pId, 
primDesc AS pDescr, secDesc AS sDescr, category AS category 
FROM masterInfo m 
INNER JOIN primDescT ON primDescT.id = m.primDescId 
INNER JOIN secDescT ON secDescT.id = m.secDescId 
WHERE m.category IN ('1','2') ORDER BY pDescr ASC LIMIT 100 OFFSET 0 

Die obige Abfrage dauert 8 Sekunden bis zur Antwort.

Aber wenn ich den Offset als 1756300 setze, dann dauert es 53 Sekunden.

SELECT m.id AS pId, 
primDesc AS pDescr, secDesc AS sDescr, category AS category 
FROM masterInfo m 
INNER JOIN primDescT ON primDescT.id = m.primDescId 
INNER JOIN secDescT ON secDescT.id = m.secDescId 
WHERE m.category IN ('1','2') ORDER BY pDescr ASC LIMIT 100 OFFSET 1756300 

Wie kann ich optimieren die obigen Abfragen innerhalb von 3 Sekunden zu holen?

Antwort

0

Das Problem mit diesen Abfragen ist die ORDER BY: Alle Ergebnisse müssen berechnet werden, bevor die Datenbank bestimmen kann, welche 100 oder 1756400 kleinsten sind. EXPLAIN QUERY PLAN Ausgabe:

 
0,0,0,SCAN TABLE masterInfo AS m 
0,1,1,SEARCH TABLE primDescT USING INTEGER PRIMARY KEY (rowid=?) 
0,2,2,SEARCH TABLE secDescT USING INTEGER PRIMARY KEY (rowid=?) 
0,0,0,USE TEMP B-TREE FOR ORDER BY 

den expliziten Sortierschritt zu entfernen, müssen Sie Index diese Spalte:

CREATE INDEX pd ON primDescT(primDesc); 

Und die Datenbank zwingen muß diesen Index zu verwenden (standardmäßig ignoriert SQLite LIMIT wenn Abfrage Abschätzen Kosten, und wenn Sie alle Ergebnisse wollen, nicht den pd Index mit schneller wäre):

SELECT ... 
FROM masterInfo m 
INNER JOIN primDescT INDEXED BY pd ON primDescT.id = m.primDescId 
--     ^^^^^^^^^^^^^ 
INNER JOIN secDescT ON secDescT.id = m.secDescId 
WHERE ... 
ORDER BY pDescr ASC 
LIMIT 100 OFFSET ...; 
 
0,0,1,SCAN TABLE primDescT USING COVERING INDEX pd 
0,1,0,SEARCH TABLE masterInfo AS m USING COVERING INDEX sqlite_autoindex_masterInfo_1 (primDescId=?) 
0,2,2,SEARCH TABLE secDescT USING INTEGER PRIMARY KEY (rowid=?) 

Ein großer OFFSET-Wert ist immer langsam; Die Datenbank muss alle diese Zeilen berechnen und wegwerfen.

Wenn Sie Paging verwenden, können Sie den OFFSET durch eine Suche in der Sortierspalte ersetzen. Dazu müssen Sie den letzten Wert der vorherigen Seite speichern:

SELECT ... 
FROM masterInfo m 
INNER JOIN primDescT INDEXED BY pd ON primDescT.id = m.primDescId 
INNER JOIN secDescT ON secDescT.id = m.secDescId 
WHERE primDesc > :LastValue 
-- ^^^^^^^^^^^^^^^^^^^^^ 
    AND ... 
ORDER BY pDescr ASC 
LIMIT 100 /* no offset */; 
+0

Ich habe die Seitennummerierung, wo Benutzer die n-te Seite navigieren können. Also, ich denke, die Verwendung von Lastvalue wird nicht sinnvoll sein. – Kamith