2014-09-10 16 views
12

Ich bin gespannt, wie die Ausführung von EXISTS() schneller als IN() sein soll.Mysql existiert vs IN - korrelierte Unterabfrage vs Unterabfrage?

Ich war answering a question, als Bill Karwin einen guten Punkt brachte. Wenn Sie EXISTS() verwenden, verwendet es eine korrelierte Unterabfrage (abhängige Unterabfrage), und IN() verwendet nur eine Unterabfrage.

EXPLAIN zeigt, dass EXISTS und NOT EXISTS sowohl eine abhängige Unterabfrage verwenden und IN/NOT IN beide nur eine Unterabfrage verwenden .. also bin ich gespannt, wie eine korrelierte Unterabfrage ist schneller als eine Unterabfrage ??

Ich habe EXISTS zuvor verwendet und es führt schneller als IN, weshalb ich verwirrt bin. Hier

ist ein SQLFIDDLE mit der erklärt

EXPLAIN SELECT COUNT(t1.table1_id) 
FROM table1 t1 
WHERE EXISTS 
( SELECT 1 
    FROM table2 t2 
    WHERE t2.table1_id <=> t1.table1_id 
); 

+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+ 
| ID | SELECT_TYPE   | TABLE | TYPE | POSSIBLE_KEYS | KEY  |KEY_LEN | REF      | ROWS | EXTRA      | 
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+ 
| 1 | PRIMARY    | t1  | index | (null)  | PRIMARY | 4 | (null)     | 4 | Using where; Using index | 
| 2 | DEPENDENT SUBQUERY | t2  | REF | table1_id  | table1_id| 4 | db_9_15987.t1.table1_id | 1 | Using where; Using index | 
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+ 

EXPLAIN SELECT COUNT(t1.table1_id) 
FROM table1 t1 
WHERE NOT EXISTS 
( SELECT 1 
    FROM table2 t2 
    WHERE t2.table1_id = t1.table1_id 
); 
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+ 
| ID | SELECT_TYPE   | TABLE | TYPE | POSSIBLE_KEYS | KEY  |KEY_LEN | REF      | ROWS | EXTRA      | 
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+ 
| 1 | PRIMARY    | t1  | index | (null)  | PRIMARY | 4 | (null)     | 4 | Using where; Using index | 
| 2 | DEPENDENT SUBQUERY | t2  | ref | table1_id  | table1_id| 4 | db_9_15987.t1.table1_id | 1 | Using index     | 
+-------+-----------------------+-----------+-------+---------------+-----------+--------+--------------------------+--------+------------------------------+ 

EXPLAIN SELECT COUNT(t1.table1_id) 
FROM table1 t1 
WHERE t1.table1_id NOT IN 
( SELECT t2.table1_id 
    FROM table2 t2 
); 
+-------+-------------------+-----------+-------+---------------+-----------+--------+----------+--------+------------------------------+ 
| ID | SELECT_TYPE  | TABLE | TYPE | POSSIBLE_KEYS | KEY  |KEY_LEN | REF  | ROWS | EXTRA      | 
+-------+-------------------+-----------+-------+---------------+-----------+--------+----------+--------+------------------------------+ 
| 1 | PRIMARY   | t1  | index | (null)  | PRIMARY | 4 | (null) | 4 | Using where; Using index | 
| 2 | SUBQUERY  | t2  | index | (null)  | table1_id| 4 | (null) | 2 | Using index     | 
+-------+-------------------+-----------+-------+---------------+-----------+--------+----------+--------+------------------------------+ 

paar Fragen

In der erklärt oben, wie funktioniert EXISTS haben using where und using index in Extras aber nicht EXISTS hat keine using where in Extras?

Wie ist eine korrelierte Unterabfrage schneller als eine Unterabfrage?

+0

Also haben Sie eine Repro von 'exists' ausgeführt schneller? In welcher Version hast du das auch erlebt? 'in' auch [hatte das gleiche Problem] (http://stackoverflow.com/q/3416076/73226) –

+0

@MartinSmith Nun, ich habe meine Abfragen von IN nach EXISTS vor etwa einem Jahr gewechselt, weil sie schneller mit EXISTS ausgeführt wurden (nicht von einer ganzen Menge so etwas wie eine halbe Sekunde zu einer Sekunde schneller) .. aber ich habe gerade einen neuen Computer und heruntergeladen die neueste Version von MySQL .. Ich habe nur eine Abfrage ausgeführt und IN lief um .004 Sekunden schneller ... Gab es kürzlich eine Lösung für den Ausführungsplan/Optimierer? –

+0

Ich weiß nicht viel über den MySql-Optimierer, aber ich glaube, dass 5.6 einige Änderungen eingeführt hat. https://dev.mysql.com/doc/refman/5.6/de/subquery-optimization.html –

Antwort

8

Dies ist ein RDBMS-agnostisch Antwort, kann aber dennoch helfen. Nach meinem Verständnis ist die korrelierte (auch abhängige) Unterabfrage vielleicht der am häufigsten falsch beschuldigte Schuldige für schlechte Leistungen.

Das Problem (wie es am häufigsten beschrieben wird) ist, dass es die innere Abfrage für jede Zeile der äußeren Abfrage verarbeitet. Wenn die äußere Abfrage also 1.000 Zeilen zurückgibt und die innere Abfrage 10.000 zurückgibt, muss die Abfrage 10.000.000 Zeilen (äußere × innere) durchlaufen, um ein Ergebnis zu erzeugen. Im Vergleich zu den 11.000 Zeilen (äußere + innere) aus einer nicht korrelierten Abfrage über die gleichen Ergebnismengen, ist das nicht gut.

Dies ist jedoch nur das Worst-Case-Szenario. In vielen Fällen kann das DBMS Indizes nutzen, um die Anzahl der Zeilen drastisch zu reduzieren. Auch wenn nur die innere Abfrage einen Index verwenden kann, werden die 10.000 Zeilen zu ~ 13 Suchvorgängen, wodurch die Gesamtzahl auf 13.000 sinkt.

Der Operator exists kann die Verarbeitung von Zeilen nach der ersten stoppen und die Abfragekosten weiter reduzieren, insbesondere wenn die meisten äußeren Zeilen mit mindestens einer inneren Zeile übereinstimmen.

In einigen seltenen Fällen habe ich festgestellt, dass SQL Server 2008R2 korrelierte Unterabfragen für einen Merge-Join optimiert (der beide Sets nur einmal durchläuft - bestes Szenario), in dem ein geeigneter Index sowohl in inneren als auch in äußeren Abfragen gefunden werden kann.

Der wahre Schuldige für schlechte Leistung ist nicht unbedingt korrelierte Unterabfragen, aber verschachtelten Scans.

3

Dies hängt von der MySQL-Version ab - es gibt einen Fehler im MySQL-Abfrageoptimierer in Versionen bis 6.0.

Unterabfragen mit "IN" wurden nicht korrekt optimiert (aber immer wieder wie abhängige ausgeführt). Dieser Fehler hat keine Auswirkungen auf exists Abfragen oder Joins.

Das Problem ist, dass für eine Anweisung, die eine in Unterabfrage verwendet, die Optimierer es als korrelierte Unterabfrage umschreibt. Betrachten Sie die folgende -Anweisung, die eine unkorrelierte Unterabfrage verwendet:

SELECT ... FROM t1 WO t1.a IN (SELECT b FROM t2);

Das Optimierungsprogramm schreibt die Anweisung eine korrelierte Unterabfrage:

SELECT FROM T1 ... WHERE EXISTIERT (1 von t2 SELECT WHERE t2.b = t1.a);

Wenn die inneren und äußeren Abfragen M und N Zeilen zurückgeben jeweils die Ausführungszeit wird in der Größenordnung von O (M × N) statt O (M + N) als es für eine unkorrelierte wäre Unterabfrage

Refs.

+2

Der Fall 'O (MxN) sieht nicht so aus, als sollte er prinzipiell für das OP gelten, da die korrelierte Unterabfrage einen Index verwenden kann. In MySQL, wenn es die innere unkorrelierte Unterabfrage vermutlich auswertet, würde es es vermutlich irgendwo noch speichern müssen und das materialisierte Ergebnis auf der inneren Seite eines Verschachtelten Schleifenverbundes auf die gleiche Weise wiedergeben. In welchem ​​Fall wird das materialisierte Ergebnis indiziert? Oder wird hier ein Hash-Join oder etwas verwendet? –

1

Wie Sie bereits wissen, verwendet eine Unterabfrage keine Werte aus der äußeren Abfrage, daher wird sie nur einmal ausgeführt. Die korrelierte Unterabfrage ist synchronisiert und wird daher für jede in der äußeren Abfrage verarbeitete Zeile ausgeführt.

Der Vorteil der Verwendung von EXISTS besteht darin, dass die Ausführung der Unterabfrage gestoppt wird, nachdem mindestens eine Zeile zurückgegeben wurde. So kann es schneller als eine einfache Unterabfrage sein. Aber es ist keine allgemeine Regel! Alle hängen von der Abfrage ab, die Sie ausführen, dem Abfrageoptimierer und der Version der SQL-Ausführungs-Engine.

EXISTS wird empfohlen, wenn Sie z. B. eine if bedingte Anweisung verwenden, da es sicherlich schneller als count ist.

Sie können nicht beide Unterabfragen mit einem einfachen Benchmark von 4 oder 3 Abfragen vergleichen.

Ich hoffe, es ist nützlich!