2013-02-14 4 views
0

J'ai un comportement étrange de mon programme, et peut-être que vous pouvez y apporter de la lumière.SQLite très lent SELECT heure

Aujourd'hui, j'ai commencé à tester du code, et j'ai réalisé qu'une requête spécifique était vraiment lente (environ 2 minutes).

ici la sélection:

select distinct table1.someName 
from table1 
INNER JOIN table2 ON table2.id = table1.t2_id 
INNER JOIN table3 ON table1.id = table3.t1_id 
INNER JOIN table4 ON Table3.id = table4.t3_id 
INNER JOIN table5 ON table5.id = table4.t5_id 
INNER JOIN table6 ON table4.id = table6.t4_id 
where t4_name = 'whatever' 
and t2_name = 'moarWhatever' 

and timestamp_till is null 

order by someName 

Donc, la chose est, le résultat est d'environ 120 dossiers. les INNER JOIN s réduisent le nombre de chèques pour timestamp_till is null à environ 20 enregistrements sur chaque enregistrement. Ce qui me dérange le plus, c'est que j'ai testé pour insérer toute la table table6 dans une nouvelle table créée et renommée timestamp_till en ende. Sur cette table la sélection est faite en environ 0.1 secondes ...

Est-ce que timestamp_till est une sorte de nom réservé de SQLite3? Serait-ce un bug dans le moteur SQLite? Est-ce ma faute? oO

modifier: ajouter la sortie EXPLAIN QUERY PLAN ...

Lorsque vous interrogez le and timestamp_till is null il donne:

0|0|4|SEARCH TABLE table5 USING COVERING INDEX sqlite_autoindex_table5 (t4_name=?) (~1 rows) 
0|1|3|SEARCH TABLE table4 USING INDEX table4.fk_table4_1_idx (t5_id=?) (~10 rows) 
0|2|2|SEARCH TABLE table3 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows) 
0|3|0|SEARCH TABLE table1 USING INTEGER PRIMARY KEY (rowid=?) (~1rows) 
0|4|1|SEARCH TABLE table2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows) 
0|5|5|SEARCH TABLE table6 USING INDEX table6.fk_table6_ts_till (timestamp_till=?) (~2 rows) 
0|0|0|USE TEMP B-TREE FOR GROUP BY 
0|0|0|USE TEMP B-TREE FOR DISTINCT 

et une rapide est:

select distinct table1.someName 
    from table1 
    INNER JOIN table2 ON table2.id = table1.t2_id 
    INNER JOIN table3 ON table1.id = table3.t1_id 
    INNER JOIN table4 ON Table3.id = table4.t3_id 
    INNER JOIN table5 ON table5.id = table4.t5_id 
    INNER JOIN table6 ON table4.id = table6.t4_id 
    where t4_name = 'whatever' 
    and t2_name = 'moarWhatever'  
    order by someName 

et son résultat:

0|0|4|SEARCH TABLE table5 USING COVERING INDEX sqlite_autoindex_table5_1 (t4name=?) (~1 rows) 
0|1|3|SEARCH TABLE table4 USING INDEX table4.fk_table4_1_idx (t5_id=?) (~10 rows) 
0|2|2|SEARCH TABLE table3 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows) 
0|3|0|SEARCH TABLE table1 USING INTEGER PRIMARY KEY (rowid=?) (~1rows) 
0|4|1|SEARCH TABLE table2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows) 
0|5|5|SEARCH TABLE table6 USING COVERING INDEX sqlite_autoindex_table6_1 (id=?) (~10 rows) 
0|0|0|USE TEMP B-TREE FOR GROUP BY 
0|0|0|USE TEMP B-TREE FOR DISTINCT 

avec le test-table qui est une copie de table6

0|0|4|SEARCH TABLE table5 USING COVERING INDEX sqlite_autoindex_table5_1 (name=?) (~1 rows) 
0|1|3|SEARCH TABLE table4 USING INDEX table4.fk_t5_idx (t5_id=?) (~10 rows) 
0|2|2|SEARCH TABLE table3 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows) 
0|3|0|SEARCH TABLE table1 USING INTEGER PRIMARY KEY (rowid=?) (~1rows) 
0|4|1|SEARCH TABLE table2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows) 
0|5|5|SEARCH TABLE test USING INDEX test.fk_test__idx (id=?) (~2 rows) 
0|0|0|USE TEMP B-TREE FOR GROUP BY 
0|0|0|USE TEMP B-TREE FOR DISTINCT 

créer script pour test

CREATE TABLE "test"(
    "id" INTEGER NOT NULL, 
    "t12_id" INTEGER NOT NULL, 
    "value" DECIMAL NOT NULL, 
    "anfang" INTEGER NOT NULL, 
    "ende" INTEGER DEFAULT NULL, 
    PRIMARY KEY("id","t12_id","anfang"), 
    CONSTRAINT "fk_test_t12_id" 
    FOREIGN KEY("t12_id") 
    REFERENCES "table12"("id"), 
    CONSTRAINT "fk_test_id" 
    FOREIGN KEY("id") 
    REFERENCES "id_col"("id"), 
    CONSTRAINT "fk_test_anfang" 
    FOREIGN KEY("anfang") 
    REFERENCES "ts_col"("id"), 
    CONSTRAINT "fk_test_ende" 
    FOREIGN KEY("ende") 
    REFERENCES "ts_col"("id") 
); 
CREATE INDEX "test.fk_test_idx_t12_id" ON "test"("t12_id"); 
CREATE INDEX "test.fk_test_idx_id" ON "test"("id"); 
CREATE INDEX "test.fk_test_anfang" ON "test"("anfang"); 
CREATE INDEX "test.fk_test_ende" ON "test"("ende"); 

zai soo longue

+0

Quelle est la sortie de [EXPLAIN QUERY PLAN] (http://www.sqlite.org/eqp.html) pour les deux requêtes? –

+0

Rejoindre 2 tables prendra un peu de temps, selon le nombre d'entrées qu'ils ont tous les deux, pas tellement sur le résultat final. Rejoindre 6 tables prendra encore plus de temps. Assurez-vous que toutes les clés étrangères sont indexées, cela devrait aider. – MPelletier

+0

@CL. ajouté à la question @MPelletier oui je sais, mais la même requête avec le timestamp_till est nul besoin de 1-2 minutes et sans même pas une seconde ... – Zaiborg

Répondre

2

Une première note: SQLite utilisera seulement 1 index dans sa requête. Jamais plus (avec la version actuelle).

Ainsi, voici ce que SQLite fait:

  • requête lente: utiliser l'index sur timestamp_till
  • requête rapide (pas de timestamp_till): utiliser l'index (automatique) table6.id.

Je vois deux solutions de contournement.

Vous pouvez utiliser une sous-requête:

select distinct SomeName FROM 
(
    select table1.someName as "SomeName", timestamp_till 
    from table1 
    INNER JOIN table2 ON table2.id = table1.t2_id 
    INNER JOIN table3 ON table1.id = table3.t1_id 
    INNER JOIN table4 ON Table3.id = table4.t3_id 
    INNER JOIN table5 ON table5.id = table4.t5_id 
    INNER JOIN table6 ON table4.id = table6.t4_id 
    where t4_name = 'whatever' 
    and t2_name = 'moarWhatever' 
) Q 
where timestamp_till is null 
order by SomeName; 

Ou vous pouvez laisser tomber votre index sur timestamp_till, si vous n'avez pas besoin ailleurs.

Il est également possible de gagner de la vitesse en réorganisant vos jointures. Habituellement, la plus petite table est la première plus rapide, mais cela peut varier considérablement.

+0

cool qui semble prometteur, mais comment expliquerait-il, que la «requête lente» sur une copie 1: 1 avec un autre nom pour «timestamp_till» peut être super rapide et sur la table d'origine non? – Zaiborg

+0

@Zaiborg Votre autre nom a-t-il également été indexé? – MPelletier

+0

ouais j'ai créé l'ensemble du test à partir d'un script de création renommé – Zaiborg