Exempel med tabellerna A och 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
Om du vill hitta föräldrar och deras barn gör du en INNER JOIN
:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent INNER JOIN child
ON parent.id = child.pid
Resultatet är att varje matchning av en förälder
s id
från den vänstra tabellen och en underordnad
s pid
från den andra tabellen visas som en rad i resultatet:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
+----+--------+------+-------+
Nu visar ovanstående inte föräldrar utan barn (eftersom deras id inte har en matchning i barnets id, så vad gör du? Du gör en yttre koppling istället. Det finns tre typer av yttre kopplingar, vänster, höger och den fullständiga yttre sammanfogningen. Vi behöver den vänstra eftersom vi vill ha de "extra" raderna från den vänstra tabellen (förälder):
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
Resultatet är att förutom tidigare matcher visas alla föräldrar som inte har en match (läs:inte har ett barn):
+----+--------+------+-------+
| 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 |
+----+--------+------+-------+
Var tog alla dessa NULL
komma från? Tja, MySQL (eller något annat RDBMS du kan använda) kommer inte att veta vad de ska lägga där eftersom dessa föräldrar inte har någon match (barn), så det finns ingen pid
inte heller barn.namn
att matcha med dessa föräldrar. Så det sätter detta speciella icke-värde som kallas NULL
.
Min poäng är att dessa NULLs
skapas (i resultatuppsättningen) under LEFT OUTER JOIN
.
Så om vi bara vill visa de föräldrar som INTE har ett barn, kan vi lägga till en WHERE child.pid IS NULL
till LEFT JOIN
ovan. JOIN
pid
är NULL kommer att visas:
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
Resultat:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Nu, vad händer om vi flyttar den ÄR NULL
kontrollera från WHERE
till den anslutande ON
klausul?
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
I det här fallet försöker databasen hitta rader från de två tabellerna som matchar dessa villkor. Det vill säga rader där parent.id =child.pid
OCH child.pid IN NULL
. Men den kan hitta ingen sådan matchning eftersom ingen child.pid
kan vara lika med något (1, 2, 3, 4 eller 5) och samtidigt vara NULL!
Så, villkoret:
ON parent.id = child.pid
AND child.pid IS NULL
motsvarar:
ON 1 = 0
som alltid är False
.
Så varför returnerar den ALLA rader från den vänstra tabellen? Eftersom det är en VÄNSTERJOIN! Och vänsterkopplingar returnerar rader som matchar (inga i det här fallet) och även rader från den vänstra tabellen som inte matchar checken (alla i det här fallet ):
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | NULL | NULL |
| 2 | Bill | NULL | NULL |
| 3 | Cath | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Jag hoppas att förklaringen ovan är tydlig.
Sidenote (inte direkt relaterat till din fråga):Varför i hela friden panoreras
dyka upp i ingen av våra JOINs? Eftersom hans pid
är NULL
och NULL i (inte vanliga) logiken i SQL är inte lika med någonting så det kan inte matcha något av de överordnade ID:n (som är 1,2,3,4 och 5). Även om det fanns en NULL där, skulle den fortfarande inte matcha eftersom NULL
är inte lika med någonting, inte ens NULL
själv (det är en mycket konstig logik, faktiskt!). Det är därför vi använder specialkontrollen IS NULL
och inte en =NULL
kontrollera.
Så, kommer att Panera
dyker upp om vi gör en RIGHT JOIN
? Ja det kommer det! Eftersom en RIGHT JOIN visar alla resultat som matchar (den första INNER JOIN vi gjorde) plus alla rader från RIGHT-tabellen som inte matchar (vilket i vårt fall är en, (NULL, 'Pan') rad.
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent RIGHT JOIN child
ON parent.id = child.pid
Resultat:
+------+--------+------+-------+
| id | parent | pid | child |
+---------------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+
Tyvärr har MySQL inte FULL JOIN
. Du kan prova det i andra RDBMS, och det kommer att visa:
+------+--------+------+-------+
| 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 |
+------+--------+------+-------+