Unterabfragen vs Joins

158

Ich habe einen langsamen Abschnitt einer Anwendung, die wir von einem anderen Unternehmen geerbt haben, überarbeitet, um einen inneren Join anstelle einer Unterabfrage wie der folgenden zu verwenden:

WHERE id IN (SELECT id FROM ...)

Die überarbeitete Abfrage wird etwa 100-mal schneller ausgeführt. (~ 50 Sekunden bis ~ 0,3) Ich habe eine Verbesserung erwartet, aber kann 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 aus oder so?

Update - Ergebnisse erklären:

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

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

vs 1 indizierte Zeile mit dem Join:

    SIMPLE  s   eq_ref  PRIMARY PRIMARY 4   newsladder_production.st.submission_id  1   Using index
Palmsey
quelle
4
Mögliches Duplikat von Join vs.
Unterabfrage
2
Kein Duplikat. Bei dieser Frage geht es speziell um auffällige Leistungsunterschiede. Die andere Frage ist allgemeiner und offen über die Vor- und Nachteile jedes Ansatzes und warum ein Ansatz populärer erscheint.
Basil Bourque
@simhumileco Das ist keine Verbesserung, es ist kein Unterschied, es widerspricht dem, was der Autor geschrieben hat und diese Art der Bearbeitung für den Codestil ist unangemessen. Wann sollte ich Änderungen am Code vornehmen?
Philipxy
Hallo @philipxy, ich wollte mich nicht in die Gedanken des Autors einmischen, sondern nur das Codefragment lesbarer und sorgfältiger schreiben.
Simhumileco

Antworten:

160

Eine "korrelierte Unterabfrage" (dh eine, bei der die where-Bedingung von Werten abhängt, die aus den Zeilen der enthaltenen Abfrage erhalten wurden) wird einmal für jede Zeile ausgeführt. Eine nicht korrelierte Unterabfrage (eine, bei der die where-Bedingung unabhängig von der enthaltenen Abfrage ist) wird zu Beginn einmal ausgeführt. Die SQL-Engine macht diese Unterscheidung automatisch.

Aber ja, der Erklärungsplan gibt Ihnen die schmutzigen Details.

Jeffrey L Whitledge
quelle
3
Bitte beachten Sie, dass DEPENDENT SUBQUERYdies genau dasselbe bedeutet wie "korrelierte Unterabfrage".
Timo
38

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

Sklivvz
quelle
5
Ich denke nicht, dass das wahr ist. Die SQL-Engine sollte die Unterabfrage nur einmal ausführen und das Ergebnis als Liste verwenden.
Dacracot
8
Das hängt davon ab, ob die Unterabfrage irgendwie mit der äußeren Abfrage korreliert ist (ihre Daten verwendet), sie wird mit jeder Zeile ausgeführt.
Qbeuek
4
In diesem Fall ist es wahrscheinlich wahr, aber im Allgemeinen nicht wahr.
Amy B
1
OPs EXPLAINsagen DEPENDENT SUBQUERY, was der klarste Indikator für dieses Verhalten ist.
Timo
16

Hier ist ein Beispiel dafür, wie Unterabfragen in MySQL 6.0 ausgewertet werden .

Das neue Optimierungsprogramm konvertiert diese Art von Unterabfragen in Verknüpfungen.

Giuseppe Maxia
quelle
Das ist ein großartiger Artikel über das verbesserte Optimierungsprogramm für MySQL 6.0, danke
Fire Crow
7

Führen Sie den EXPLAIN-Plan für jede Version aus. Dort erfahren Sie, warum.

Scotta
quelle
6

Bevor die Abfragen für das Dataset ausgeführt werden, werden sie einem Abfrageoptimierer unterzogen. Der Optimierer versucht, die Abfrage so zu organisieren, dass so schnell wie möglich so viele Tupel (Zeilen) aus der Ergebnismenge entfernt werden können. Wenn Sie Unterabfragen verwenden (insbesondere schlechte), können die Tupel häufig erst aus der Ergebnismenge entfernt werden, wenn die äußere Abfrage ausgeführt wird.

Ohne die Abfrage zu sehen, ist es schwer zu sagen, was an dem Original so schlecht war, aber ich vermute, es war etwas, das der Optimierer einfach nicht viel besser machen konnte. Wenn Sie 'EXPLAIN' ausführen, wird die Optimierungsmethode zum Abrufen der Daten angezeigt.

pfranza
quelle
4

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

Wo in und Mitglied werden kann typischerweise unter Verwendung des gleichen Ausführungsplan umgesetzt werden, so der Regel gibt es Null - Beschleunigungs-von zwischen ihnen zu verändern.

Amy B.
quelle
3
Haha, ich <3 Sql scheuert diese Abwahl, weil sie nicht wissen, wie man Abfragepläne liest.
Amy B
4

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

Cade Roux
quelle
4

Normalerweise ist es das Ergebnis, dass der Optimierer nicht herausfinden kann, dass die Unterabfrage als Join ausgeführt werden kann. In diesem Fall führt er die Unterabfrage für jeden Datensatz in der Tabelle aus, anstatt die Tabelle in der Unterabfrage mit der von Ihnen abgefragten Tabelle zu verknüpfen. Einige der "unternehmerischeren" Datenbanken sind besser darin, aber sie vermissen sie manchmal immer noch.

Mark Roddy
quelle
4

Diese Frage ist etwas allgemein gehalten, daher hier eine allgemeine Antwort:

Grundsätzlich dauern Abfragen länger, wenn MySQL Tonnen von Zeilen zum Sortieren hat.

Mach das:

Führen Sie eine EXPLAIN für jede der Abfragen aus (die JOIN'ed, dann die Subqueried) und veröffentlichen Sie die Ergebnisse hier.

Ich denke, den Unterschied in der Interpretation dieser Abfragen durch MySQL zu sehen, wäre eine Lernerfahrung für alle.

Pete Karl II
quelle
4

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

Shawn
quelle
3

Die Unterabfrage führte wahrscheinlich einen "vollständigen Tabellenscan" aus. Mit anderen Worten, wenn Sie den Index nicht verwenden und viel zu viele Zeilen zurückgeben, die die Where from the-Hauptabfrage herausfiltern musste.

Nur eine Vermutung ohne Details natürlich, aber das ist die übliche Situation.

igelkott
quelle
2

Bei einer Unterabfrage müssen Sie das 2. SELECT für jedes Ergebnis erneut ausführen, und jede Ausführung gibt normalerweise 1 Zeile zurück.

Bei 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 das ist, was eine Datenbank gut kann. Vielleicht kann der Optimierer jetzt erkennen, wie ein Index jetzt besser genutzt werden kann.

Joel Coehoorn
quelle
2

Es ist weniger die Unterabfrage als die IN-Klausel, obwohl Joins die Grundlage von mindestens der SQL-Engine von Oracle bilden und extrem schnell ausgeführt werden.

Dacracot
quelle
1
wo in ist wirklich nicht von Natur aus schlecht.
Shawn
2

Entnommen aus dem Referenzhandbuch ( 14.2.10.11 Umschreiben von Unterabfragen als Verknüpfungen ):

Ein LEFT [OUTER] JOIN kann schneller sein als eine entsprechende Unterabfrage, da der Server sie möglicherweise besser optimieren kann - eine Tatsache, die nicht nur für MySQL Server spezifisch ist.

Unterabfragen können also langsamer sein als LEFT [OUTER] JOINS.

simhumileco
quelle