2017-02-01 7 views
1

私は3つのテーブル、masterInfo、primDescT、secDescTを持つデータベースを持っています。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); 

テーブルでは、masterInfoの行数は1765137、primDescTの行数は312210、secDescTの行数は105458です。

以下のクエリを使用して結果を取得しました。

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 

上記のクエリは応答に8秒かかります。

私が1756300としてオフセットを設定した場合、それは53秒かかるでしょう。

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 

3秒以内にフェッチするように上記のクエリを最適化するにはどうすればよいですか?

答えて

0

これらの問合せの問題はORDER BYです。すべての結果を計算してから、データベースで100または1756400のうち最も小さいものを判別する必要があります。 EXPLAIN QUERY PLAN出力:

 
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 

明示的なソートステップを削除するには、インデックス、その列なければならない:

CREATE INDEX pd ON primDescT(primDesc); 

そして、あなたは、デフォルトでは(このインデックスを使用するようにデータベースを強制する必要がありますクエリを推定する際に、SQLiteはLIMITを無視コスト、あなたはすべての結果、より高速になりpdインデックスを使用していない)望んだ場合:

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=?) 

大きなOFFSET値は常に低速です。データベースはこれらの行をすべて計算して廃棄する必要があります。

ページングを使用している場合は、OFFSETをソート列のルックアップに置き換えることができます。これは、前のページの最後の値を保存する必要があります。

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

私は、ページをナビゲートすることができます。だから、私は、最後の値を使うことは役に立たないと思います。 – Kamith

関連する問題