列のインデックスを作成するときに、このsqliteクエリがはるかに遅いのはなぜですか?


14

(偽の)人の名前を含む、それぞれ50,000行の2つのテーブルを持つsqliteデータベースがあります。両方のテーブルに共通する名前(名前、ミドルネームのイニシャル、姓)がいくつあるかを調べる簡単なクエリを作成しました。

select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;

主キー以外にインデックスがない場合(このクエリとは無関係)、すぐに実行されます。

[james@marlon Downloads] $ time sqlite3 generic_data_no_indexes.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    0m0.115s
user    0m0.111s
sys     0m0.004s

しかし、各テーブルの3つの列にインデックスを追加する場合(全部で6つのインデックス):

CREATE INDEX `idx_uk_givenname` ON `fakenames_uk` (`givenname` )
//etc.

その後、非常にゆっくりと実行されます。

[james@marlon Downloads] $ time sqlite3 generic_data.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    1m43.102s
user    0m52.397s
sys     0m50.696s

これに韻や理由はありますか?

EXPLAIN QUERY PLANインデックスなしのバージョンの結果は次のとおりです。

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING AUTOMATIC COVERING INDEX (middleinitial=? AND surname=? AND givenname=?)

これはインデックス付きです:

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING INDEX idx_us_middleinitial (middleinitial=?)

1
インデックスがカバーしていません。各列を個別にインデックス付けしているようです。インデックス内の3つの列すべてを含むカバーインデックスを作成するとどうなりますか(middleinitialsurnamegivenname)?
ランドルフウェスト

回答:


14

SQLiteでは、結合はネストされたループ結合として実行されます。つまり、データベースは1つのテーブルを通過し、行ごとに他のテーブルから一致する行を検索します。

インデックスがある場合、データベースはインデックス内の一致をすばやく検索し、対応するテーブル行に移動して、必要な他の列の値を取得できます。

この場合、3つの可能なインデックスがあります。統計情報(ANALYZEを実行して作成される)がない場合、データベースはI / Oを削減するために最小のものを選択します。ただし、middleinitialインデックスはフェッチする必要があるテーブル行の数を大幅に削減しないため、役に立ちません。また、テーブルの行が順番に読み取られるのではなく、ランダムに読み取られるため、インデックスを介した追加のステップにより実際に必要なI / Oが増加します

インデックスがない場合、一致する行の検索には、最初のテーブルの各行に対して2番目のテーブルの完全なテーブルスキャンが必要になります。これは非常に悪いので、データベースは、このクエリのためだけに一時インデックスを作成して削除する価値があると推定します。この一時(「AUTOMATIC」)インデックスは、検索に使用されるすべての列に作成されます。COUNT(*)操作は他の列の値を必要としないため、このインデックスはたまたまカバリングインデックスであるため、インデックスエントリに対応するテーブル行を実際に検索する必要がなく、さらに節約できます。 / O。

このクエリを高速化するには、このインデックスを永続的に作成します。これにより、一時的なインデックスを作成する必要がなくなります。

CREATE INDEX uk_all_names ON fakenames_uk(surname, givenname, middleinitial);

EXPLAIN QUERY PLAN
SELECT count(*)
FROM fakenames_uk
JOIN fakenames_usa USING (givenname, middleinitial, surname);

0|0|1|SCAN TABLE fakenames_usa
0|1|0|SEARCH TABLE fakenames_uk USING COVERING INDEX uk_all_names (surname=? AND givenname=? AND middleinitial=?)

surname3列のインデックスはこの列のルックアップに使用できるため、onのインデックスは不要になりました。この列でのみルックアップを行う場合、
上のインデックスgivennameが役立つ場合があります。
上のインデックスmiddleinitialは常に価値がありません。26個の可能な値の1つを検索するクエリは、テーブル全体をスキャンするだけで高速になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.