2008-09-26 9 views
150

verbindet Refactoring ich einen langsamen Abschnitt einer Anwendung, die wir von einem anderen Unternehmen übernahm eine innere Verknüpfung statt einer Unterabfrage wieSubqueries vs

where id in (select id from ...) 

Die umstrukturierte Abfrage läuft über 100x schneller zu bedienen. (~ 50 Sekunden bis ~ 0,3) Ich erwartete eine Verbesserung, aber kann mir jemand erklären, warum es so drastisch war? Die in der where-Klausel verwendeten Spalten wurden alle indiziert. Führt SQL die Abfrage in der where-Klausel einmal pro Zeile oder so aus?

aktualisieren - Erklären Ergebnisse:

Der Unterschied ist im zweiten Teil der "where id in()" Abfrage -

2 DEPENDENT SUBQUERY submission_tags ref st_tag_id st_tag_id 4 const 2966 Using where 

vs 1 indexierte Zeile mit dem Join:

SIMPLE s eq_ref PRIMARY PRIMARY 4 newsladder_production.st.submission_id 1 Using index 
+4

Mögliches Duplikat von [Join vs. Sub-Query] (http://stackoverflow.com/questions/2577174/join-vs-sub-query) –

+1

Kein Duplikat. In dieser Frage geht es insbesondere um auffällige Leistungsunterschiede. Die andere Frage ist allgemeiner, offen über die Vor- und Nachteile jedes Ansatzes und warum ein Ansatz populärer erscheint. –

Antwort

147

Eine "korrelierte Unterabfrage" (d. H., eine, in der die Where-Bedingung von Werten abhängt, die aus den Zeilen der enthaltenden Abfrage erhalten wurden), wird für jede Zeile einmal ausgeführt. Eine nicht korrelierte Unterabfrage (eine, in der die WHERE-Bedingung unabhängig von der enthaltenden Abfrage ist) wird einmal am Anfang ausgeführt. Die SQL-Engine trifft diese Unterscheidung automatisch.

Aber, yeah, explain-plan wird dir die schmutzigen Details geben.

+9

+1 für die Verwendung von EXPLAIN – susmits

+1

Bitte beachten Sie, dass 'ABHÄNGIGE SUBQUERY 'genau das gleiche wie' korrelierte Unterabfrage 'bedeutet. – Timo

7

Führen Sie den EXPLAIN-Plan für jede Version, es wird Ihnen sagen, warum.

3

Sehen Sie sich den Abfrageplan für jede Abfrage an.

Wo in und beitreten können typischerweise implementiert werden denselben Ausführungsplan verwendet, so typischerweise gibt es Null-Beschleunigungs-zwischen ihnen ändert.

+3

Haha, ich <3 Sql scrubs, die abstimmen, weil sie nicht wissen, wie man Abfragepläne liest. –

+5

Ich habe Sie für die Verwendung des Wortes "Peelings" –

+3

downvoted Ich habe für Sie mit dem Wort Peeling upvoted –

36

Sie führen die Unterabfrage einmal für jede Zeile, während der Join auf Indizes erfolgt.

+5

Ich glaube nicht, das ist wahr. Die SQL-Engine sollte die Unterabfrage nur einmal ausführen und das Ergebnis als Liste verwenden. – dacracot

+8

Das hängt davon ab - wenn die Unterabfrage irgendwie mit der äußeren Abfrage korreliert ist (verwendet ihre Daten), wird sie mit jeder Zeile ausgeführt. – qbeuek

+4

Es ist wahrscheinlich in diesem Fall wahr, aber es ist im Allgemeinen nicht wahr. –

3

Optimierer hat keine sehr gute Arbeit geleistet. In der Regel können sie ohne Unterschied transformiert werden und der Optimierer kann dies tun.

+1

Aber nicht in MySQL – Greg

2

Mit einer Unterabfrage müssen Sie die zweite SELECT für jedes Ergebnis erneut ausführen, und jede Ausführung gibt in der Regel 1 Zeile zurück.

Mit einem Join gibt das 2. SELECT viel mehr Zeilen zurück, aber Sie müssen es nur einmal ausführen. Der Vorteil ist, dass Sie jetzt an den Ergebnissen teilnehmen können, und das Verbinden von Beziehungen ist, was eine Datenbank gut sein soll. Zum Beispiel kann der Optimierer jetzt herausfinden, wie man einen Index besser ausnutzen kann.

2

Es ist nicht so sehr die Unterabfrage als die IN-Klausel, obwohl Joins die Grundlage von mindestens Oracle SQL-Engine sind und extrem schnell ausgeführt werden.

+1

wo in wirklich nicht von Natur aus schlecht ist. – Shawn

3

Die Unterabfrage führte wahrscheinlich einen "vollständigen Tabellenscan" durch. Mit anderen Worten, den Index nicht zu verwenden und viel zu viele Zeilen zurückzugeben, die die Wo aus der Hauptabfrage herausfiltern mussten.

Nur eine Schätzung ohne Details natürlich, aber das ist die übliche Situation.

4

Normalerweise ist es das Ergebnis des Optimierers nicht in der Lage herauszufinden, dass die Unterabfrage als ein Join ausgeführt werden kann. In diesem Fall führt es die Unterabfrage für jeden Datensatz in der Tabelle statt dann die Tabelle in der Unterabfrage gegen die Tabelle du fragst. Einige der "Enterprise" -Datenbanken sind besser darin, aber sie vermissen es manchmal noch.

4

Diese Frage ist etwas allgemein, so ist hier eine allgemeine Antwort:

Grundsätzlich Abfragen länger dauern, wenn MySQL Tonnen Reihen hat durch zu sortieren.

tun:

Führen Sie eine EXPLAIN auf jeder der Abfragen (die JOIN'ed eine, dann die Subqueried eins), und die Ergebnisse hier posten.

Ich denke, dass der Unterschied in der Interpretation dieser Abfragen durch MySQL eine Lernerfahrung für alle ist.

6

bevor die Abfragen für das Dataset ausgeführt werden, über das sie einem Abfrageoptimierer übergeben werden, versucht das Optimierungsprogramm, die Abfrage so zu organisieren, dass sie so viele Tupel (Zeilen) wie möglich aus dem Resultset entfernen kann . Bei der Verwendung von Unterabfragen (besonders bei schlechten) können die Tupel oft nicht aus der Ergebnismenge entfernt werden, bis die äußere Abfrage gestartet wird.

Ohne die Abfrage zu sehen, ist es schwer zu sagen, was am Original so schlecht war, aber ich vermutete, dass es etwas war, das der Optimierer nicht viel besser machen konnte. Wenn Sie 'explain' ausführen, wird Ihnen die Optimizer-Methode zum Abrufen der Daten angezeigt.

4

Die where-Unterabfrage muss eine Abfrage für jede zurückgegebene Zeile ausführen. Der innere Join muss nur 1 Abfrage ausführen.

16

Hier ist ein Beispiel dafür, wie subqueries are evaluated in MySQL 6.0.

Der neue Optimierer konvertiert diese Art von Unterabfragen in Joins.

+0

Das ist ein großartiger Artikel über den verbesserten Optimierer von MySQL 6.0, danke –

2

aus dem Referenzhandbuch Taken (14.2.10.11 Rewriting Subqueries as Joins):

A LEFT [OUTER] JOIN kann schneller sein als eine entsprechende Unterabfrage, da der Server zu optimieren besser eine Tatsache, möglicherweise in der Lage, die nicht spezifisch MySQL Server allein.

So Unterabfragen können langsamer sein als LINKS [OUTER] JOINS.