it-swarm.com.de

Warum ist diese SQLite-Abfrage viel langsamer, wenn ich die Spalten indiziere?

Ich habe eine SQLite-Datenbank mit zwei Tabellen mit jeweils 50.000 Zeilen, die Namen von (falschen) Personen enthalten. Ich habe eine einfache Abfrage erstellt, um herauszufinden, wie viele Namen (Vorname, mittlere Initiale, Nachname) beiden Tabellen gemeinsam sind:

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;

Wenn außer den Primärschlüsseln keine Indizes vorhanden sind (für diese Abfrage irrelevant), wird sie schnell ausgeführt:

[[email protected] 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

Aber wenn ich den drei Spalten in jeder Tabelle Indizes hinzufüge (insgesamt sechs Indizes):

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

dann läuft es schmerzhaft langsam:

[[email protected] 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

Gibt es einen Reim oder Grund dafür?

Hier ist das Ergebnis von EXPLAIN QUERY PLAN für die Version ohne Indizes:

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

Dies ist mit Indizes:

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

In SQLite werden Verknüpfungen als verschachtelte Schleifenverknüpfungen ausgeführt, d. H. Die Datenbank durchläuft eine Tabelle und sucht für jede Zeile nach übereinstimmenden Zeilen aus der anderen Tabelle.

Wenn ein Index vorhanden ist, kann die Datenbank schnell nach Übereinstimmungen im Index suchen und dann in die entsprechende Tabellenzeile wechseln, um die Werte aller anderen benötigten Spalten abzurufen.

In diesem Fall gibt es drei mögliche Indizes. Ohne statistische Informationen (die durch Ausführen von ANALYSE erstellt würden) wählt die Datenbank die kleinste aus, um die E/A zu reduzieren. Der Index middleinitial ist jedoch nutzlos, da er die Anzahl der abzurufenden Tabellenzeilen nicht wesentlich verringert. und der zusätzliche Schritt durch den Index erhöht tatsächlich die erforderliche E/A, da die Tabellenzeilen nicht mehr in der Reihenfolge, sondern zufällig gelesen werden.

Wenn es keinen Index gibt, würde die Suche nach übereinstimmenden Zeilen einen vollständigen Tabellenscan der zweiten Tabelle für jede Zeile der ersten Tabelle erfordern. Dies wäre so schlimm, dass die Datenbank schätzt, dass es sich lohnt, einen temporären Index nur für diese Abfrage zu erstellen und dann zu löschen. Dieser temporäre Index ("AUTOMATIC") wird für alle für die Suche verwendeten Spalten erstellt. Die COUNT (*) -Operation benötigt keine Werte aus anderen Spalten, daher ist dieser Index zufällig ein abdeckender Index , was bedeutet, dass es nicht erforderlich ist, die einem Index entsprechende Tabellenzeile tatsächlich nachzuschlagen Eintrag, der noch mehr E/A spart.

Um diese Abfrage zu beschleunigen, erstellen Sie diesen Index dauerhaft, sodass kein temporärer Index mehr erstellt werden muss:

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

Der Index für surname wird nicht mehr benötigt, da der dreispaltige Index für alle Suchvorgänge in dieser Spalte verwendet werden kann.
Der Index für givenname kann hilfreich sein, wenn Sie nur in dieser Spalte nachschlagen.
Der Index für middleinitial ist immer wertlos: Eine Abfrage, die nach einem der 26 möglichen Werte sucht, ist schneller, wenn nur die gesamte Tabelle gescannt wird.

16
CL.