2010-04-17 7 views
7

Ich habe zwei mögliche Abfragen, beide geben die Ergebnismenge, die ich will.Warum benötigt SQLite so viel Zeit, um die Daten abzurufen?

Die Abfrage dauert etwa 30 ms, aber 150 ms, um die Daten aus der Datenbank abzurufen.

SELECT 
    id 
FROM 
    featurevalues as featval3 
WHERE 
    featval3.feature IN (?,?,?,?) 
AND 
    EXISTS 
    (
     SELECT 
      1 
     FROM 
      product_to_value, 
      product_to_value as prod2, 
      features, 
      featurevalues 
     WHERE 
      product_to_value.feature = features.int 
     AND 
      product_to_value.value = featurevalues.id 
     AND 
      features.id = ? 
     AND 
      featurevalues.id IN (?,?) 
     AND 
      product_to_value.product = prod2.product 
     AND 
      prod2.value = featval3.id 
    ) 

Abfrage zwei etwa 3 ms -einem nimmt, ist diejenige, die ich deshalb vorzuziehen, sondern nimmt auch 170ms die Daten zu holen.

SELECT 
    (
     SELECT 
      prod2.value 
     FROM 
      product_to_value, 
      product_to_value as prod2, 
      features, 
      featurevalues 
     WHERE 
      product_to_value.feature = features.int 
     AND 
      product_to_value.value = featurevalues.id 
     AND 
      features.id = ? 
     AND 
      featurevalues.id IN (?,?) 
     AND 
      product_to_value.product = prod2.product 
     AND 
      prod2.value = featval3.id 
    ) as id 
FROM 
    featurevalues as featval3 
WHERE 
    featval3.feature IN (?,?,?,?) 

Die 170ms scheint mit der Anzahl der Zeilen aus der Tabelle featval3 verwandt zu sein. Nachdem ein Index für featval3.feature IN (?,?,?,?) Verwendet wurde, verbleiben 151 Elemente in featval3.

Gibt es etwas Offensichtliches, das mir bezüglich des langsamen Abrufs fehlt? Soweit ich weiß, ist alles richtig indiziert .. Ich bin verwirrt, weil die zweite Abfrage dauert nur 3ms zu laufen.

aktualisieren

Das ist, was ich, wenn ich auf die zweite Abfrage ein EXPLAIN renne:

0 Trace 0 0 0 00 
1 Variable 4 1 4 00 
2 Goto 0 88 0 00 
3 OpenRead 5 6883 0 00 
4 If 6 16 0 00 
5 Integer 1 6 0 00 
6 OpenEphemeral 7 1 0 00 
7 Null 0 8 0 00 
8 MakeRecord 1 1 8 00 
9 IdxInsert 7 8 0 00 
10 MakeRecord 2 1 8 00 
11 IdxInsert 7 8 0 00 
12 MakeRecord 3 1 8 00 
13 IdxInsert 7 8 0 00 
14 MakeRecord 4 1 8 00 
15 IdxInsert 7 8 0 00 
16 Rewind 7 86 0 00 
17 Column 7 0 5 00 
18 IsNull 5 85 0 00 
19 Affinity 5 1 0 00 
20 SeekGe 5 85 5 00 
21 IdxGE 5 85 5 01 
22 Null 0 10 0 00 
23 Integer 1 11 0 00 
24 MustBeInt 11 0 0 00 
25 IfZero 11 82 0 00 
26 Variable 1 12 3 00 
27 OpenRead 2 25 0 00 
28 OpenRead 8 7005 0 00 
29 OpenRead 9 26 0 00 
30 OpenRead 10 6732 0 00 
31 OpenRead 11 6766 0 00 
32 Column 5 1 15 00 
33 IsNull 15 77 0 00 
34 Affinity 15 1 0 00 
35 SeekGe 8 77 15 00 
36 IdxGE 8 77 15 01 
37 IdxRowid 8 8 0 00 
38 Seek 2 8 0 00 
39 Column 2 0 16 00 
40 IsNull 16 76 0 00 
41 Affinity 16 1 0 00 
42 SeekGe 9 76 16 00 
43 IdxGE 9 76 16 01 
44 Column 9 1 17 00 
45 IsNull 17 75 0 00 
46 SCopy 12 18 0 00 
47 IsNull 18 75 0 00 
48 Affinity 17 2 0 00 
49 SeekGe 10 75 17 00 
50 IdxGE 10 75 17 01 
51 If 20 59 0 00 
52 Integer 1 20 0 00 
53 OpenEphemeral 13 1 0 00 
54 Null 0 21 0 00 
55 MakeRecord 13 1 21 00 
56 IdxInsert 13 21 0 00 
57 MakeRecord 14 1 21 00 
58 IdxInsert 13 21 0 00 
59 Rewind 13 74 0 00 
60 Column 13 0 19 00 
61 IsNull 19 73 0 00 
62 Affinity 19 1 0 00 
63 SeekGe 11 73 19 00 
64 IdxGE 11 73 19 01 
65 Column 9 2 21 00 
66 Column 11 0 7 00 
67 Ne 7 72 21 6a 
68 Column 8 0 22 00 
69 Move 22 10 1 00 
70 AddImm 11 -1 0 00 
71 IfZero 11 77 0 00 
72 Next 11 64 0 00 
73 Next 13 60 0 00 
74 Next 10 50 0 00 
75 Next 9 43 0 00 
76 Next 8 36 0 00 
77 Close 2 0 0 00 
78 Close 8 0 0 00 
79 Close 9 0 0 00 
80 Close 10 0 0 00 
81 Close 11 0 0 00 
82 SCopy 10 9 0 00 
83 ResultRow 9 1 0 00 
84 Next 5 21 0 00 
85 Next 7 17 0 00 
86 Close 5 0 0 00 
87 Halt 0 0 0 00 
88 Transaction 0 0 0 00 
89 VerifyCookie 0 319 0 00 
90 TableLock 0 14 0 00 
91 TableLock 0 25 0 00 
92 TableLock 0 11 0 00 
93 Goto 0 3 0 00 

nicht sicher, was es bedeutet aber.

+0

Wie werden die Daten abgerufen? – dan04

+0

Mit PHP: 'while ($ row = $ statement-> fetch (PDO :: FETCH_OBJ))' – SQLighter

Antwort

4

Ich bin nicht sicher über SQLite, aber die meisten Datenbanken berechnen nicht die gesamte Ergebnismenge sofort, wenn Sie eine Abfrage ausführen. Sie kompilieren und optimieren die Abfrage und starten sie dann, indem sie die ersten paar Zeilen abrufen.

So ähnlich wie es ist schnell eine große Datei zu öffnen, aber es dauert eine lange Zeit, um den gesamten Inhalt zu lesen.

-1

Verwenden Sie Transaktion beginnen und Transaktion beenden, immer wenn Sie eine SQL-Abfrage schreiben. Dies erklärt sich hier und viele andere nützliche Informationen:

SQLite Optimization FAQ

Sie die Indizierung überprüfen Details zu where-Klausel hier:

SQLite Query Optimizer Overview

+0

Wenn Sie BEGIN/END TRANSACTION nicht verwenden, erhält jede Anweisung ihre eigene Transaktion. Die Verwendung expliziter Transaktionen verbessert die Effizienz mit mehreren Anweisungen, aber das OP führt nur eine aus. – dan04

+0

Ich schreibe nicht. – SQLighter

3

Sie sollten wahrscheinlich hier Joins und Ihre Die Verwendung korrelierter Unterabfragen könnte die Dinge verlangsamen. Obwohl es stimmt, dass der Optimierer oft einen Kreuz-Join mit einer Where-Klausel ausführen kann, als ob es ein Join wäre, und korrelierte Unterabfragen auch als Joins ausführen kann, würde ich nicht davon ausgehen, dass SQLite dies in allen Fällen tun kann. Umschreiben Sie Ihre Abfrage mit JOIN Ich denke, ergibt dies:

SELECT DISTINCT prod2.value AS id 
FROM product_to_value 
JOIN features ON product_to_value.feature = features.int 
JOIN product_to_value as prod2 ON product_to_value.product = prod2.product 
JOIN featurevalues as featval3 ON prod2.value = featval3.id 
JOIN featurevalues ON product_to_value.value = featurevalues.id 
WHERE features.id = ? 
AND featurevalues.id IN (?,?) 
AND featval3.feature IN (?,?,?,?) 

die Sie interessieren und sehen es schneller (und gibt immer noch das richtige Ergebnis).

+0

SQLite übersetzt JOINs in die WHERE-Klausel. Ich bin mir ziemlich sicher, dass die Abfrage tut, was zu tun ist. – SQLighter

+0

Das Problem, das mit der von Ihnen geposteten Abfrage auftritt, ist, dass doppelte Werte zurückgegeben werden. Ich könnte das mit einer DISTINCT oder einer temporären Tabelle lösen, aber das wäre sehr langsam, besonders wenn es sich um eine riesige Ergebnismenge handelt. Ich dachte mir, ich könnte das umgehen mit einer Unterabfrage in jeder Zeile eines kleineren Ergebnismenge von featval3.IN (?,?,?,?). – SQLighter

+1

@Derk: OK, das habe ich verpasst. Es war schwierig zu verstehen, was deine Anfrage gemacht hat. Ich habe das DISTINCT hinzugefügt. Korrelierte Unterabfragen können langsam sein, da Sie möglicherweise mehrere Scans über die Tabellen/Indizes benötigen und nicht nur einen Scan. –

Verwandte Themen