Tabellenausdrücke in der FROM
-Klausel einer
Abfrage werden in vielen Fällen vereinfacht.
Auf der Parserebene werden Abfragen mit rechten äußeren Join-Operationen in gleichwertige Abfragen konvertiert, die nur Left-Join-Operationen enthalten. Im Allgemeinen erfolgt die Konvertierung auf der Basis der folgenden Regel:
(T1, ...) RIGHT JOIN (T2,...) ON P(T1,...,T2,...) = (T2, ...) LEFT JOIN (T1,...) ON P(T1,...,T2,...).
Alle inneren Join-Ausdrücke der Form T1 INNER JOIN T2
ON P(T1,T2)
werden durch die Liste
T1,T2
ersetzt, und
P(T1,T2)
wird als Konjunkt mit der
WHERE
-Bedingung (oder mit der Join-Bedingung
des einbettenden Joins, sofern vorhanden) verknüpft.
Wenn der Optimierer Pläne für Join-Abfragen mit einer äußeren Join-Operation auswertet, berücksichtigt er nur diejenigen Pläne, bei denen bei einer solchen Operation zunächst auf die äußeren und erst dann auf die inneren Tabellen zugegriffen wird. Die Optimiereroptionen sind eingeschränkt, weil nur solche Pläne uns die Ausführung von Abfragen mit äußeren Join-Operationen über das Schema mit verschachtelten Schleifen gestatten.
Angenommen, wir haben eine Abfrage folgenden Aussehens:
SELECT * T1 LEFT JOIN T2 ON P1(T1,T2) WHERE P(T1,T2) AND R(T2)
Hierbei schränkt R(T2)
die Anzahl passender
Datensätze aus der Tabelle T2
erheblich ein.
Wenn wir die Abfrage in ihrer ursprünglichen Form ausgeführt
hätten, hätte der Optimierer keine andere Chance gehabt, als
zuerst auf die Tabelle T1
und erst dann auf
T2
zuzugreifen. Dies hätte einen sehr
ineffizienten Ausführungsplan zur Folge gehabt.
Glücklicherweise wandelt MySQL eine solche Abfrage in eine
Abfrage ohne äußere Join-Operation um, wenn die
WHERE
-Bedingung nullabweisend wird. Eine
Bedingung heißt nullabweisend für eine äußere
Join-Operation, wenn sie für jeden
NULL
-ergänzten Datensatz, der für die
Operation erstellt wurde, stets FALSE
oder
UNKNOWN
ist.
Betrachten Sie folgenden äußeren Join:
T1 LEFT JOIN T2 ON T1.A=T2.A
Für ihn sind Bedingungen wie die folgenden nullabweisend:
T2.B IS NOT NULL, T2.B > 3, T2.C <= T1.C, T2.B < 2 OR T2.C > 1
Bedingungen wie die folgenden sind hingegen nicht nullabweisend:
T2.B IS NULL, T1.B < 3 OR T2.B IS NOT NULL, T1.B < 3 OR T2.B > 3
Die allgemeinen Regeln zur Überprüfung, ob eine Bedingung für einen äußeren Join nullabweisend ist oder nicht, sind recht einfach. Eine Bedingung ist nullabweisend, wenn sie
die Form A IS NOT NULL
hat, wobei
A
ein Attribut einer der inneren Tabellen
ist,
ein Prädikat ist, das eine innere Tabelle referenziert, die
UNKNOWN
ist, wenn eines ihrer Argumente
NULL
ist,
eine Konjunktion mit einer nullabweisenden Bedingung als Konjunkt ist,
eine Disjunktion nullabweisender Bedingungen ist.
Eine Bedingung kann für eine äußere Join-Operation nullabweisend sein und gleichzeitig für eine andere nicht. Betrachten Sie folgende Abfrage:
SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A LEFT JOIN T3 ON T3.B=T1.B WHERE T3.C > 0
Hier ist die WHERE
-Bedingung für die zweite
äußere Join-Operation nullabweisend, nicht jedoch für die
erste.
Wenn die WHERE
-Bedingung für eine äußere
Join-Operation in einer Abfrage nullabweisend ist, wird die
äußere Join-Operation durch eine innere Join-Operation
ersetzt.
So wird etwa obige Abfrage durch die folgende ersetzt:
SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A INNER JOIN T3 ON T3.B=T1.B WHERE T3.C > 0
Für die ursprüngliche Abfrage würde der Optimierer Pläne
auswerten, die mit der Zugriffsreihenfolge
T1,T2,T3
kompatibel wären. Für die
ersetzende Abfrage wird außerdem die Zugriffssequenz
T3,T1,T2
in Betracht gezogen.
Eine Konvertierung einer äußeren Join-Operation kann eine Konvertierung einer anderen derartigen Operation auslösen. Betrachten Sie folgende Abfrage:
SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A LEFT JOIN T3 ON T3.B=T2.B WHERE T3.C > 0
Diese wird in die folgende Abfrage konvertiert:
SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A INNER JOIN T3 ON T3.B=T2.B WHERE T3.C > 0
Diese wiederum ist äquivalent mit dieser Abfrage:
SELECT * FROM (T1 LEFT JOIN T2 ON T2.A=T1.A), T3 WHERE T3.C > 0 AND T3.B=T2.B
Nun kann die verbleibende äußere Join-Operation ebenfalls
durch einen inneren Join ersetzt werden, weil die Bedingung
T3.B=T2.B
nullabweisend ist und wir eine
Abfrage erhalten, die überhaupt keine äußeren Joins enthält:
SELECT * FROM (T1 INNER JOIN T2 ON T2.A=T1.A), T3 WHERE T3.C > 0 AND T3.B=T2.B
Manchmal gelingt es, eine eingebettete äußere Join-Operation zu ersetzen, aber der einbettende äußere Join kann nicht konvertiert werden. Betrachten Sie folgende Abfrage:
SELECT * FROM T1 LEFT JOIN (T2 LEFT JOIN T3 ON T3.B=T2.B) ON T2.A=T1.A WHERE T3.C > 0
Sie wird wie folgt konvertiert:
SELECT * FROM T1 LEFT JOIN (T2 INNER JOIN T3 ON T3.B=T2.B) ON T2.A=T1.A WHERE T3.C > 0,
Dies lässt sich nur in einer Form umschreiben, die nach wie vor die einbettende äußere Join-Operation enthält:
SELECT * FROM T1 LEFT JOIN (T2,T3) ON (T2.A=T1.A AND T3.B=T2.B) WHERE T3.C > 0.
Wenn Sie versuchen, eine eingebettete äußere Join-Operation zu
konvertieren, dann müssen Sie die Join-Bedingung für den
einbettenden äußeren Join gemeinsam mit der
WHERE
-Bedingung berücksichtigen. Betrachten
Sie folgende Abfrage:
SELECT * FROM T1 LEFT JOIN (T2 LEFT JOIN T3 ON T3.B=T2.B) ON T2.A=T1.A AND T3.C=T1.C WHERE T3.D > 0 OR T1.D > 0
Hier ist die WHERE
-Bedingung für den
eingebetteten äußeren Join nicht nullabweisend, wohl aber die
Join-Bedingung des einbettenden äußeren Joins
T2.A=T1.A
AND T3.C=T1.C
.
Insofern kann die Abfrage wie folgt konvertiert werden:
SELECT * FROM T1 LEFT JOIN (T2, T3) ON T2.A=T1.A AND T3.C=T1.C AND T3.B=T2.B WHERE T3.D > 0 OR T1.D > 0
Dies ist eine Übersetzung des MySQL-Referenzhandbuchs, das sich auf dev.mysql.com befindet. Das ursprüngliche Referenzhandbuch ist auf Englisch, und diese Übersetzung ist nicht notwendigerweise so aktuell wie die englische Ausgabe. Das vorliegende deutschsprachige Handbuch behandelt MySQL bis zur Version 5.1.