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
sql
mysql
performance
database-design
join
Palmsey
quelle
quelle
Antworten:
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.
quelle
DEPENDENT SUBQUERY
dies genau dasselbe bedeutet wie "korrelierte Unterabfrage".Sie führen die Unterabfrage einmal für jede Zeile aus, während der Join für Indizes erfolgt.
quelle
EXPLAIN
sagenDEPENDENT SUBQUERY
, was der klarste Indikator für dieses Verhalten ist.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.
quelle
Führen Sie den EXPLAIN-Plan für jede Version aus. Dort erfahren Sie, warum.
quelle
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.
quelle
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.
quelle
Optimizer hat keine sehr gute Arbeit geleistet. Normalerweise können sie ohne Unterschied transformiert werden, und der Optimierer kann dies tun.
quelle
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.
quelle
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.
quelle
Die where-Unterabfrage muss 1 Abfrage für jede zurückgegebene Zeile ausführen. Der innere Join muss nur 1 Abfrage ausführen.
quelle
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.
quelle
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.
quelle
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.
quelle
Entnommen aus dem Referenzhandbuch ( 14.2.10.11 Umschreiben von Unterabfragen als Verknüpfungen ):
Unterabfragen können also langsamer sein als LEFT [OUTER] JOINS.
quelle