2010-07-15 14 views
13
SELECT 
a.foo 
b.bar 
c.foobar 
FROM tableOne AS a 
INNER JOIN tableTwo AS b ON a.pk = b.fk 
LEFT JOIN tableThree AS c ON b.pk = c.fk 
WHERE a.foo = 'something' 
AND c.foobar = 'somethingelse' 

Wenn die Klausel nach der WHERE-Klausel verwendet wird, scheint die linke Verknüpfung in eine innere Verknüpfung zu wechseln. Das Verhalten, das ich sehe, ist, wenn es nicht etwas in TableThree gibt, werden 0 Zeilen zurückgegeben.linker Join wird zu innerem Join

Wenn ich c.foobar = 'somethingselse' in die Join-Klausel verschiebe, verhält sich der gespeicherte Join wie ein linker Join.

Kann mir jemand auf eine Dokumentation verweisen, die beschreibt, warum das passiert? Vielen Dank

Antwort

31

Es ist wegen Ihrer WHERE Klausel.

Jedes Mal, wenn Sie einen Wert von der rechten Seite eines Links in einer WHERE Join-Klausel angeben (die NOT NULL ist), können Sie unbedingt alle der NULL Werte eliminieren, und es wird im wesentlichen eine INNER JOIN.

Wenn Sie schreiben, wird AND c.foobar = 'somethingelse' OR c.foobar IS NULL das Problem lösen.

Sie können auch den c.foobar Teil in Ihr Join-Prädikat verschieben, und das wird das Problem ebenfalls lösen.

+0

+1. nette Erklärung. –

+0

@DaveMarkle ist der gleiche Fall, wenn ich Wert von der rechten Seite eines linken Joins in der Gruppe angeben oder haben. (ich meine es wird links nach innen konvertieren) –

+0

@Dave Markle Wissen Sie, was wäre schneller, die WHERE-Klausel oder das JOIN-Prädikat in einem Fall wie diesem? – PerPlexSystem

3

Der Grund dafür ist, dass der linke Join alle Spalten von c auf NULL für die Zeilen setzt, die in c nicht existieren (d. H., Die nicht verbunden werden können). Dies bedeutet, dass der Vergleich c.foobar = 'somethingelse' nicht wahr ist, weshalb diese Zeilen nicht zurückgegeben werden.

In dem Fall, in dem Sie die c.foobar = 'somethingelse' in die Join-Bedingung verschieben, gibt dieser Join immer noch diese Zeilen (wenn auch mit NULL-Werten) zurück, wenn die Bedingung nicht wahr ist.

0

Die Joins machen ihre Arbeit, dann wo die Datensätze entfernt wird wo c.foobar <> 'Somethingselse'.

Der Effekt sieht wie ein innerer Join aus, ist es aber nicht.

0

Ein Linker Join gibt alles aus der linken Tabelle (tableTwo in Ihrem Beispiel) und alle übereinstimmenden Zeilen aus der Tabelle rechts (in Ihrem Beispiel tableThree) zurück. Wenn Sie etwas auf der rechten Seite des linken Joins filtern (z. B. tableThree) und nicht übereinstimmende Werte berücksichtigen, benötigen Sie effektiv, dass ein Wert existiert und dass der Wert "etwas" ist, was einem inneren entspricht Beitreten. Wenn das, was Sie versuchen, alle tableTwo Reihen zu tun ist, zu finden, die keine Zeile in tableThree mit einem foobar Wert von ‚etwas‘ haben, können Sie die Filterung in die on-Klausel verschieben:

Select a.foo, b.bar, c.foobar 
From tableOne As a 
    Inner Join tableTwo as b 
     On b.fk = a.pk 
    Left Join tableThree as c 
     On c.fk = b.pk 
      And c.foobar = 'something' 
Where a.foo = 'something' 
    And c.pk Is Null 

Die letzte Zugabe c.pk Is Null Filter für Werte, die keinen tableThree-Wert mit einem foobar-Wert von "something" haben. Wenn tableThree-Werte nur angezeigt werden sollen, wenn sie einen foobar-Wert von 'something' haben (und andernfalls nullt), entfernen Sie den zusätzlichen Filter I hinzugefügt von c.pk Is Null.

0

1. Fall (wo die c.foobar in der ist, wo Klausel) die Filterung durch die c.foobar passiert, nachdem der Joins aufgetreten sind (sie auftreten richtig), so dass Sie effektiv ausfiltern alle resultierenden Datensätze, die tun habe keine 'somethingelse' drin ..

2. Fall der c.foobar ist Teil der Verbindung, so wertet zur Zeit verbindet und steuert nur den Ausgang der Verbindung, und nicht die letzte Re-Cord (gibt null wo die Verbindung fehlschlägt) ..

0

DIE LINKE JOIN erzeugt NULL, wenn keine übereinstimmenden Zeilen vorhanden sind. In diesem Fall wird c.foobar für die nicht übereinstimmenden Zeilen NULL sein. Aber Ihre WHERE-Klausel sucht nach einem bestimmten Wert: 'somethingselse', und so werden alle NULL-Werte herausgefiltert. Da ein INNER JOIN auf der rechten Seite auch keine NULL-Werte erzeugt, sehen die beiden gleich aus. Sie können 'OR c.foobar IS NULL' hinzufügen, um die Nullwerte wieder zuzulassen.

Wenn Sie die Bedingung in die ON-Klausel verschieben, wird sie Teil des JOIN-Zeilenabgleichs und nicht der endgültige Filter. Die Join-Übereinstimmung kann fehlschlagen, und der Outer-Join gibt dann NULLs für Fälle zurück, in denen "c.foobar" NULL ist oder nicht "Somethingselse".

Siehe

1

Das 'Wo' -Klausel nach dem Join durchgeführt wird. Dies ist nicht wichtig für innere Joins, sondern für äußere Joins.

Shorten Beispiel

SELECT b.bar,c.foobar FROM tableTwo AS b LEFT JOIN tableThree AS c ON b.pk=c.fk WHERE c.foobar='somethingelse' 

Raw data     Outer Join Result  Select Result 
b.pk c.fk c.foorbar  b.pk c.fk c.foorbar  c.foorbar 
1 1 something  1 1 something  <not in result set> 
1 1 somethingelse 1 1 somethingelse somethingelse 


SELECT b.bar,c.foobar FROM tableTwo AS b LEFT JOIN tableThree AS c ON b.pk=c.fk AND c.foobar='somethingelse' 

Raw data     Outer Join Result  Select Result 
b.pk c.fk c.foorbar  b.pk c.fk c.foorbar  c.foorbar 
1 1 something  1 null null   null 
1 1 somethingelse 1 1 somethingelse somethingelse 
0

Es ist nicht links abbiegen JOIN in ein INNER JOIN, dachte der Effekt auftreten können gleich sein. Wenn Sie die WHERE-Bedingung

UND c.foobar = ‚somethingelse‘

Sie bekommen alle Fälle zu befreien, die eine LEFT JOIN erlauben zu handeln, wie es funktioniert. In diesem Fall sind einige der Werte für c.foobar NULL. Wenn Sie dies für die JOIN-Bedingung festlegen, können Sie weiterhin LEFT JOIN-Ergebnisse erzielen, die nur für C-Ergebnisse zurückgegeben werden.

Verwandte Themen