Przykład z tabelami A i B:
A (parent) B (child)
============ =============
id | name pid | name
------------ -------------
1 | Alex 1 | Kate
2 | Bill 1 | Lia
3 | Cath 3 | Mary
4 | Dale NULL | Pan
5 | Evan
Jeśli chcesz znaleźć rodziców i ich dzieci, wykonaj INNER JOIN
:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent INNER JOIN child
ON parent.id = child.pid
Rezultat jest taki, że każde dopasowanie rodzica
id
z lewej tabeli i dziecko
pid
z drugiej tabeli pokaże się jako wiersz w wyniku:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
+----+--------+------+-------+
Teraz powyższe nie pokazuje rodziców bez dzieci (ponieważ ich identyfikatory nie pasują do identyfikatorów dzieci, więc co robisz? Zamiast tego wykonujesz sprzężenie zewnętrzne. Istnieją trzy typy sprzężeń zewnętrznych, lewe, prawe i pełne złącze zewnętrzne. Potrzebujemy lewego, ponieważ chcemy „dodatkowych” wierszy z lewej tabeli (rodzica):
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
Powoduje to, że oprócz poprzednich dopasowań, wszyscy rodzice, którzy nie mają dopasowania (czytaj:nie mają dziecka) są również pokazani:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Gdzie się podziały te wszystkie NULL
pochodzić z? Cóż, MySQL (lub jakikolwiek inny RDBMS, którego możesz użyć) nie będzie wiedział, co tam umieścić, ponieważ ci rodzice nie mają dopasowania (dziecko), więc nie ma pid
ani nazwa.dziecka
pasować do tych rodziców. Tak więc umieszcza tę specjalną wartość niebędącą wartością o nazwie NULL
.
Chodzi mi o to, że te NULL
są tworzone (w zestawie wyników) podczas LEFT OUTER JOIN
.
Tak więc, jeśli chcemy pokazać tylko tych rodziców, którzy NIE mają dziecka, możemy dodać GDZIE child.pid JEST NULL
do LEFT JOIN
nad. GDZIE
klauzula jest oceniana (sprawdzana) po JOIN
skończone. Tak więc z powyższego wyniku jasno wynika, że tylko ostatnie trzy wiersze, w których pid
zostanie wyświetlone NULL:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
WHERE child.pid IS NULL
Wynik:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
A teraz, co się stanie, jeśli przeniesiemy, że IS NULL
sprawdź z GDZIE
do dołączania ON
klauzula?
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
AND child.pid IS NULL
W takim przypadku baza danych próbuje znaleźć wiersze z dwóch tabel, które spełniają te warunki. To znaczy wiersze, w których parent.id =child.pid
ORAZ child.pid IN NULL
. Ale może znaleźć brak takiego dopasowania ponieważ nie ma child.pid
może być równe czemuś (1, 2, 3, 4 lub 5) i jednocześnie być NULL!
Tak więc warunek:
ON parent.id = child.pid
AND child.pid IS NULL
jest równoważne z:
ON 1 = 0
co zawsze jest Fałsz
.
Dlaczego więc zwraca WSZYSTKIE wiersze z lewej tabeli? Ponieważ jest to LEWE DOŁĄCZENIE! A sprzężenia lewe zwracają pasujące wiersze (w tym przypadku brak) a także wiersze z lewej tabeli, które nie pasują czek (wszystkie w tym przypadku ):
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | NULL | NULL |
| 2 | Bill | NULL | NULL |
| 3 | Cath | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Mam nadzieję, że powyższe wyjaśnienie jest jasne.
Uwaga poboczna (niezwiązana bezpośrednio z Twoim pytaniem):Dlaczego u licha nie Pan
nie pojawiają się w żadnym z naszych JOIN? Ponieważ jego pid
jest NULL
i NULL w (nie wspólnej) logice SQL nie jest równe niczemu, więc nie może się zgadzać z żadnym z nadrzędnych identyfikatorów (czyli 1,2,3,4 i 5). Nawet gdyby był tam NULL, nadal nie pasowałby, ponieważ NULL
nie równa się niczemu, nawet NULL
(to naprawdę bardzo dziwna logika!). Dlatego używamy specjalnego sprawdzenia IS NULL
a nie =NULL
sprawdź.
Czy więc Pan
? pokaż się, jeśli wykonamy RIGHT JOIN
? Tak, to będzie! Ponieważ RIGHT JOIN pokaże wszystkie pasujące wyniki (pierwsze INNER JOIN, które zrobiliśmy) oraz wszystkie wiersze z tabeli RIGHT, które nie pasują (co w naszym przypadku jest jednym, (NULL, 'Pan') wiersz.
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent RIGHT JOIN child
ON parent.id = child.pid
Wynik:
+------+--------+------+-------+
| id | parent | pid | child |
+---------------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+
Niestety, MySQL nie ma FULL JOIN
. Możesz go wypróbować w innych systemach RDBMS, a pokaże się:
+------+--------+------+-------+
| id | parent | pid | child |
+------+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+