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, zweiter Vorname, Nachname) in beiden Tabellen vorkommen:
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 dies schnell ausgeführt:
[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
Wenn ich jedoch den drei Spalten jeder Tabelle Indizes hinzufüge (insgesamt sechs Indizes):
CREATE INDEX `idx_uk_givenname` ON `fakenames_uk` (`givenname` )
//etc.
dann läuft es schmerzhaft langsam:
[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
Gibt es einen Reim oder Grund dafür?
Hier ist das Ergebnis 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=?)
performance
index
optimization
sqlite
count
chiastic-security
quelle
quelle
middleinitial
,surname
undgivenname
) enthält?Antworten:
In SQLite werden Joins als Joins mit verschachtelten Schleifen ausgeführt, dh, die Datenbank durchsucht eine Tabelle und sucht für jede Zeile nach übereinstimmenden Zeilen aus der anderen Tabelle.
Wenn ein Index vorhanden ist, kann die Datenbank Übereinstimmungen im Index schnell nachschlagen und dann in die entsprechende Tabellenzeile wechseln, um die Werte aller anderen erforderlichen Spalten abzurufen.
In diesem Fall gibt es drei mögliche Indizes. Ohne statistische Informationen (die durch Ausführen von ANALYZE erstellt würden ) wählt die Datenbank die kleinste aus, um die E / A zu reduzieren. Der
middleinitial
Index 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 benötigte E / A, da die Tabellenzeilen nicht mehr in der richtigen Reihenfolge, sondern zufällig gelesen werden.Wenn es keinen Index gibt, erfordert die Suche nach übereinstimmenden Zeilen einen vollständigen Tabellenscan der zweiten Tabelle für jede Zeile der ersten Tabelle. Dies wäre so schlimm, dass die Datenbank einschätzt, dass es sich lohnt, nur für diese Abfrage einen temporären Index 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 handelt es sich bei diesem Index zufällig um einen abdeckenden Index. Dies bedeutet, dass die Tabellenzeile, die einem Indexeintrag entspricht, nicht nachgeschlagen werden muss, wodurch noch mehr I gespart wird /Ö.
Um diese Abfrage zu beschleunigen, erstellen Sie diesen Index dauerhaft, sodass kein temporärer Index mehr erstellt werden muss:
Der Index für
surname
wird nicht mehr benötigt, da der dreispaltige Index für 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 durchsucht wird.quelle