SQL Left Join gegen mehrere Tabellen in der FROM-Zeile?

256

Die meisten SQL-Dialekte akzeptieren beide folgenden Abfragen:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

Wenn Sie nun einen Outer Join benötigen, ist natürlich die zweite Syntax erforderlich. Aber warum sollte ich bei einem inneren Join die zweite Syntax der ersten vorziehen (oder umgekehrt)?

jmucchiello
quelle
1
Guffa: Wie hast du das gefunden? Obwohl meine Frage mehr Best Practice ist als "Wie mache ich"
jmucchiello
Da es sich um eine bewährte Methode handelt, machen Sie dies bitte zu einem Wiki.
Binoj Antony
1
Ich glaube nicht, dass jemand etwas über die Leistung dieser beiden gesagt hat. Kann jemand etwas Vernünftiges in Bezug auf signifikante Unterschiede bestätigen oder zitieren?
Ahnbizcad
@ahnbizcad Die beiden angegebenen Abfragen machen nicht dasselbe. Der erste gibt den gleichen Wert zurück wie ein INNER JOIN ON. Die Implementierung ist DBMS-versioniert und hat auch dann nur wenige Garantien. Aber DBMS-Transformationen, die Fälle von Komma gegen INNER JOIN ON / WHERE gegen CROSS JOIN WHERE gleichwertig sind, sind trivial. Erfahren Sie mehr über die Optimierung / Implementierung relationaler Datenbankabfragen.
Philipxy
Haben Sie eine Ressourcenempfehlung? Gigantische, dichte Handbücher sind der Grund, warum ich versuche, von hier zu lernen.
Ahnbizcad

Antworten:

319

Die alte Syntax, bei der nur die Tabellen WHEREaufgelistet und die Verknüpfungskriterien mithilfe der Klausel angegeben werden, ist in den meisten modernen Datenbanken veraltet.

Es ist nicht nur für die Show gedacht, die alte Syntax kann mehrdeutig sein, wenn Sie sowohl INNER- als auch OUTER-Joins in derselben Abfrage verwenden.

Lassen Sie mich Ihnen ein Beispiel geben.

Angenommen, Sie haben 3 Tabellen in Ihrem System:

Company
Department
Employee

Jede Tabelle enthält zahlreiche miteinander verknüpfte Zeilen. Sie haben mehrere Unternehmen, und jedes Unternehmen kann mehrere Abteilungen haben, und jede Abteilung kann mehrere Mitarbeiter haben.

Ok, jetzt möchten Sie Folgendes tun:

Listen Sie alle Unternehmen auf und schließen Sie alle Abteilungen und Mitarbeiter ein. Beachten Sie, dass einige Unternehmen noch keine Abteilungen haben, stellen Sie jedoch sicher, dass Sie diese auch einbeziehen. Stellen Sie sicher, dass Sie nur Abteilungen mit Mitarbeitern abrufen, aber immer alle Unternehmen auflisten.

Also machst du das:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

Beachten Sie, dass der letzte eine innere Verknüpfung ist, um die Kriterien zu erfüllen, nach denen Sie nur Abteilungen mit Personen wünschen.

Ok, was passiert jetzt? Das Problem ist, dass es vom Datenbankmodul, dem Abfrageoptimierer, den Indizes und den Tabellenstatistiken abhängt. Lassen Sie mich erklären.

Wenn der Abfrageoptimierer feststellt, dass der Weg dazu darin besteht, zuerst ein Unternehmen zu übernehmen, dann die Abteilungen zu finden und dann eine innere Verbindung mit den Mitarbeitern herzustellen, erhalten Sie keine Unternehmen, die keine Abteilungen haben.

Der Grund dafür ist, dass die WHEREKlausel bestimmt, welche Zeilen im Endergebnis landen, nicht einzelne Teile der Zeilen.

In diesem Fall ist die Spalte Department.ID aufgrund des linken Joins NULL. Wenn es also um INNER JOIN to Employee geht, gibt es keine Möglichkeit, diese Einschränkung für die Employee-Zeile zu erfüllen, und dies wird auch nicht der Fall sein erscheinen.

Wenn sich das Abfrageoptimierungsprogramm dagegen entscheidet, zuerst den Join von Abteilungsmitarbeitern in Angriff zu nehmen und dann einen Link-Join mit den Unternehmen durchzuführen, werden diese angezeigt.

Die alte Syntax ist also nicht eindeutig. Es gibt keine Möglichkeit, anzugeben, was Sie möchten, ohne sich mit Abfragehinweisen zu befassen, und einige Datenbanken haben überhaupt keine Möglichkeit.

Geben Sie die neue Syntax ein, mit der Sie auswählen können.

Wenn Sie beispielsweise alle Unternehmen möchten, wie in der Problembeschreibung angegeben, würden Sie Folgendes schreiben:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

Hier geben Sie an, dass der Join von Abteilungsmitarbeitern als ein Join ausgeführt werden soll, und verbinden dann die Ergebnisse mit den Unternehmen.

Angenommen, Sie möchten nur Abteilungen, deren Name den Buchstaben X enthält. Auch hier besteht bei Joins im alten Stil die Gefahr, dass Sie das Unternehmen verlieren. Wenn es keine Abteilungen mit einem X im Namen hat, aber mit der neuen Syntax können Sie dies tun:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

Diese zusätzliche Klausel wird für die Verknüpfung verwendet, ist jedoch kein Filter für die gesamte Zeile. Die Zeile wird möglicherweise mit Unternehmensinformationen angezeigt, enthält jedoch möglicherweise NULL-Werte in allen Abteilungs- und Mitarbeiterspalten für diese Zeile, da für diese Firma keine Abteilung mit einem X im Namen vorhanden ist. Dies ist mit der alten Syntax schwierig.

Aus diesem Grund hat Microsoft unter anderen Anbietern die alte äußere Join-Syntax, jedoch nicht die alte innere Join-Syntax seit SQL Server 2005 und höher abgelehnt. Die einzige Möglichkeit, mit einer Datenbank zu kommunizieren, die unter Microsoft SQL Server 2005 oder 2008 unter Verwendung der Outer Join-Syntax im alten Stil ausgeführt wird, besteht darin, diese Datenbank in den 8.0-Kompatibilitätsmodus (auch bekannt als SQL Server 2000) zu versetzen.

Darüber hinaus war die alte Methode, eine Reihe von Tabellen mit einer Reihe von WHERE-Klauseln auf den Abfrageoptimierer zu werfen, vergleichbar mit der Aussage "Hier sind Sie, tun Sie das Beste, was Sie können". Mit der neuen Syntax muss der Abfrageoptimierer weniger arbeiten, um herauszufinden, welche Teile zusammenpassen.

Da haben Sie es also.

LEFT and INNER JOIN ist die Welle der Zukunft.

Lasse V. Karlsen
quelle
28
"wird in den meisten modernen Datenbanken nicht mehr unterstützt." --- nur neugierig, welche?
Zerkms
10
Verzeih mir, bin mit dem Operator * = nicht vertraut, was macht er? Vielen Dank!
Ultrajohn
9
Stern = und = Stern sind (nun ja) rechte und linke äußere Verbindungen, oder ist das links und rechts? Ich bin seit Ewigkeiten veraltet und habe sie seit SQL Server 6 nicht mehr verwendet.
Tony Hopkinson
3
Komma ist nicht veraltet. Nie-Standard - OUTER JOINSyntax *=/ =*/ *=*ist veraltet.
Philipx
1
Diese Antwort beantwortet nicht einmal die Frage, bei der es nicht um äußere Verknüpfungen geht. Die einzige Behauptung über Komma gegen INNER JOIN ON, Neuoptimierung, ist falsch.
Philipxy
17

Die JOIN-Syntax hält die Bedingungen in der Nähe der Tabelle, auf die sie angewendet werden. Dies ist besonders nützlich, wenn Sie eine große Anzahl von Tabellen verbinden.

Übrigens können Sie auch mit der ersten Syntax einen Outer Join ausführen:

WHERE a.x = b.x(+)

Oder

WHERE a.x *= b.x

Oder

WHERE a.x = b.x or a.x not in (select x from b)
Andomar
quelle
2
Die Syntax * = ist in MS SQLServer aus gutem Grund veraltet: Sie erschwert nicht nur das Lesen, sondern macht auch nicht das, was die Leute denken, und es ist NICHT dasselbe wie ein ähnlich aussehender LEFT JOIN. Die (+) Syntax ist mir unbekannt; Welche SQL-Implementierung macht das?
Euro Micelli
2
Die andere Syntax wird zumindest von Oracle verwendet.
Lasse V. Karlsen
4
Verwenden Sie niemals die SQL Server-Syntax * =, sie liefert KEINE konsistenten Ergebnisse, da sie manchmal als Cross-Join und nicht als Left-Join interpretiert wird. Dies gilt bereits für SQL Server 2000. Wenn Sie Code verwenden, der diesen verwendet, müssen Sie dies beheben.
HLGEM
12

Der erste Weg ist der ältere Standard. Die zweite Methode wurde in SQL-92, http://en.wikipedia.org/wiki/SQL, eingeführt . Der vollständige Standard kann unter http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt eingesehen werden .

Es dauerte viele Jahre, bis Datenbankunternehmen den SQL-92-Standard übernahmen.

Der Grund, warum die zweite Methode bevorzugt wird, ist der SQL-Standard gemäß dem ANSI- und ISO-Standardkomitee.

Dwight T.
quelle
,ist immer noch Standard. onmusste nur outer joineinmal eingeführt werden, sobald auch Unterauswahlen eingeführt wurden.
philipxy
12

Wenn Ihre FROM-Klausel Tabellen wie folgt auflistet:

SELECT * FROM
  tableA, tableB, tableC

Das Ergebnis ist ein Kreuzprodukt aller Zeilen in den Tabellen A, B, C. Dann wenden Sie die Einschränkung an, WHERE tableA.id = tableB.a_iddie eine große Anzahl von Zeilen wegwirft, und dann weiter ... AND tableB.id = tableC.b_idund Sie sollten dann nur die Zeilen erhalten, die Sie wirklich interessieren im.

DBMS wissen, wie diese SQL so optimiert werden kann, dass der Leistungsunterschied zum Schreiben mit JOINs (falls vorhanden) vernachlässigbar ist. Unter Verwendung der Notation JOIN macht die SQL - Anweisung mehr lesbar (IMHO, nicht Joins schaltet die Anweisung in ein Chaos). Bei Verwendung des Cross-Produkts müssen Sie Join-Kriterien in der WHERE-Klausel angeben, und das ist das Problem mit der Notation. Sie überfüllen Ihre WHERE-Klausel mit Dingen wie

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

Dies wird nur verwendet, um das Kreuzprodukt einzuschränken. Die WHERE-Klausel sollte nur EINSCHRÄNKUNGEN für die Ergebnismenge enthalten. Wenn Sie Tabellenverknüpfungskriterien mit Ergebnismengeneinschränkungen mischen, ist es für Sie (und andere) schwieriger, Ihre Abfrage zu lesen. Sie sollten auf jeden Fall JOINs verwenden und die FROM-Klausel als FROM-Klausel und die WHERE-Klausel als WHERE-Klausel beibehalten.

Peter Perháč
quelle
10

Die zweite wird bevorzugt, da es weitaus weniger wahrscheinlich ist, dass eine versehentliche Querverbindung entsteht, wenn vergessen wird, die where-Klausel einzufügen. Ein Join mit no on-Klausel schlägt die Syntaxprüfung fehl, ein Join im alten Stil mit no where-Klausel schlägt nicht fehl und führt einen Cross-Join durch.

Wenn Sie später einen linken Join benötigen, ist es für die Wartung hilfreich, dass sich alle in derselben Struktur befinden. Und die alte Syntax ist seit 1992 veraltet. Es ist längst vorbei, sie nicht mehr zu verwenden.

Außerdem habe ich festgestellt, dass viele Leute, die ausschließlich die erste Syntax verwenden, Joins nicht wirklich verstehen und das Verständnis von Joins entscheidend ist, um bei Abfragen korrekte Ergebnisse zu erzielen.

HLGEM
quelle
6

Ich denke, es gibt einige gute Gründe auf dieser Seite, die zweite Methode zu verwenden - explizite JOINs. Der Clou ist jedoch, dass es viel einfacher wird, die verbleibenden Auswahlkriterien in der WHERE-Klausel zu sehen, wenn die JOIN-Kriterien aus der WHERE-Klausel entfernt werden.

In wirklich komplexen SELECT-Anweisungen wird es für einen Leser viel einfacher zu verstehen, was vor sich geht.

Alan G.
quelle
5

Die SELECT * FROM table1, table2, ...Syntax ist für einige Tabellen in Ordnung, wird jedoch exponentiell ( nicht unbedingt eine mathematisch genaue Aussage Anzahl von Tabellen ) immer schwieriger zu lesen.

Die JOIN-Syntax ist (am Anfang) schwieriger zu schreiben, macht jedoch deutlich, welche Kriterien welche Tabellen beeinflussen. Dies macht es viel schwieriger, einen Fehler zu machen.

Wenn alle Joins INNER sind, sind beide Versionen gleichwertig. Sobald Sie jedoch irgendwo in der Anweisung einen OUTER-Join haben, werden die Dinge viel komplizierter und es ist praktisch garantiert, dass das, was Sie schreiben, nicht das abfragt, was Sie zu schreiben glauben.

Euro Micelli
quelle
2

Wenn Sie einen Outer Join benötigen, ist die zweite Syntax nicht immer erforderlich:

Orakel:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (obwohl es in der Version 2000 veraltet ist ) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x

Aber zurück zu Ihrer Frage. Ich kenne die Antwort nicht, aber es hängt wahrscheinlich damit zusammen, dass ein Join natürlicher ist (zumindest syntaktisch) als das Hinzufügen eines Ausdrucks zu einer where- Klausel, wenn Sie genau das tun: Joining .

Pablo Santa Cruz
quelle
SQL Server hat diese Syntax für Linksverknüpfungen nicht mehr unterstützt und liefert selbst in SQL Server 2000 nicht konsistent korrekte Ergebnisse (manchmal wird eine Kreuzverknüpfung anstelle einer Linksverknüpfung durchgeführt) und sollte in SQL Server niemals verwendet werden.
HLGEM
@HLGEM: Danke für die Info. Ich werde meinen Beitrag aktualisieren, um zu reflektieren, was Sie sagen.
Pablo Santa Cruz
0

Ich höre viele Leute sich beschweren, dass der erste zu schwer zu verstehen ist und dass er unklar ist. Ich sehe kein Problem damit, aber nach dieser Diskussion verwende ich das zweite aus Gründen der Klarheit sogar für INNER JOINS.

kemiller2002
quelle
1
Ich hatte die Angewohnheit, die JOIN-Syntax nicht zu verwenden und es auf die erste Weise zu tun. Ich muss zugeben, dass ich immer noch oft in der Gewohnheit stecke, nur weil ich denke, mein Gehirn wurde konditioniert, um dieser Logik zu folgen, wobei mir die Join-Syntax manchmal schwer zu denken scheint.
TheTXI
3
Das wurde mir auch so beigebracht. Ich habe meinen Codierungsstil geändert, weil die Leute ihn sich ansehen und nicht leicht erkennen konnten, was los war. Da es keinen logischen Unterschied gibt und ich keinen Grund finde, den ersteren dem letzteren vorzuziehen, war ich der Meinung, dass ich mich daran anpassen sollte, den Code klarer zu gestalten, damit andere verstehen, was ich schreibe.
kemiller2002
0

Für die Datenbank sind sie am Ende gleich. Für Sie müssen Sie jedoch in einigen Situationen diese zweite Syntax verwenden. Um Abfragen zu bearbeiten, die letztendlich verwendet werden müssen (um herauszufinden, dass Sie eine linke Verknüpfung benötigen, bei der Sie eine gerade Verknüpfung hatten), und um die Konsistenz zu gewährleisten, würde ich nur die zweite Methode verwenden. Dies erleichtert das Lesen von Abfragen.

Jeff Ferland
quelle
0

Nun, die erste und die zweite Abfrage können zu unterschiedlichen Ergebnissen führen, da ein LEFT JOIN alle Datensätze aus der ersten Tabelle enthält, auch wenn die richtige Tabelle keine entsprechenden Datensätze enthält.

Gavin H.
quelle