Voorbeeld met tabellen A en 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
Als je ouders en hun kinderen wilt vinden, doe je een INNER JOIN
:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent INNER JOIN child
ON parent.id = child.pid
Het resultaat is dat elke match van een parent
's id
uit de linkertabel en een child
's pid
uit de tweede tabel wordt weergegeven als een rij in het resultaat:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
+----+--------+------+-------+
Nu worden in het bovenstaande geen ouders zonder kinderen weergegeven (omdat hun id's geen overeenkomst hebben in de id's van het kind, dus wat doe je? In plaats daarvan doe je een outer join. Er zijn drie soorten outer joins, de linker, de rechter en de volledige outer join. We hebben de linker nodig omdat we de "extra" rijen van de linkertabel (ouder) willen:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
Het resultaat is dat naast eerdere matches, ook alle ouders die geen match hebben (lees:geen kind hebben) getoond worden:
+----+--------+------+-------+
| 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 |
+----+--------+------+-------+
Waar zijn al die NULL
Komt van? Welnu, MySQL (of een ander RDBMS dat u misschien gebruikt) zal niet weten wat ze daar moeten plaatsen, aangezien deze ouders geen match hebben (kid), dus er is geen pid
noch child.name
passen bij die ouders. Dus, het plaatst deze speciale niet-waarde genaamd NULL
.
Mijn punt is dat deze NULLs
worden gemaakt (in de resultatenset) tijdens de LEFT OUTER JOIN
.
Dus als we alleen de ouders willen tonen die GEEN kind hebben, kunnen we een WHERE child.pid IS NULL
toevoegen naar de LEFT JOIN
boven. De WHERE
clausule wordt geëvalueerd (aangevinkt) na de JOIN
is klaar. Het is dus duidelijk uit het bovenstaande resultaat dat alleen de laatste drie rijen waar de pid
is NULL wordt weergegeven:
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
Resultaat:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Wat gebeurt er als we die IS NULL
verplaatsen? controleer vanaf de WHERE
naar de deelname ON
clausule?
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
In dit geval probeert de database rijen te vinden uit de twee tabellen die aan deze voorwaarden voldoen. Dat wil zeggen, rijen waar parent.id = child.pid
EN child.pid IN NULL
. Maar het kan zo'n overeenkomst niet vinden omdat geen child.pid
kan gelijk zijn aan iets (1, 2, 3, 4 of 5) en tegelijkertijd NULL zijn!
Dus de voorwaarde:
ON parent.id = child.pid
AND child.pid IS NULL
is gelijk aan:
ON 1 = 0
wat altijd False
. is .
Dus, waarom retourneert het ALLE rijen uit de linkertabel? Omdat het een LEFT JOIN is! En left joins retourneren rijen die overeenkomen (geen in dit geval) en ook rijen uit de linkertabel die niet overeenkomen het vinkje (allemaal in dit geval ):
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | NULL | NULL |
| 2 | Bill | NULL | NULL |
| 3 | Cath | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Ik hoop dat de bovenstaande uitleg duidelijk is.
Sidenote (niet direct gerelateerd aan uw vraag):Waarom in hemelsnaam niet Pan
verschijnen in geen van onze JOINs? Omdat zijn pid
is NULL
en NULL in de (niet gebruikelijke) logica van SQL is nergens gelijk aan, dus het kan niet overeenkomen met een van de bovenliggende id's (die 1,2,3,4 en 5 zijn). Zelfs als er een NULL was, zou deze nog steeds niet overeenkomen omdat NULL
is nergens gelijk aan, zelfs niet NULL
zelf (het is inderdaad een heel vreemde logica!). Daarom gebruiken we de speciale controle IS NULL
en niet een = NULL
controleren.
Dus, zal Pan
verschijnen als we een RIGHT JOIN
doen ? Ja het zal! Omdat een RIGHT JOIN alle resultaten toont die overeenkomen (de eerste INNER JOIN die we deden) plus alle rijen uit de RIGHT-tabel die niet overeenkomen (wat in ons geval één is, de (NULL, 'Pan')
rij.
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent RIGHT JOIN child
ON parent.id = child.pid
Resultaat:
+------+--------+------+-------+
| id | parent | pid | child |
+---------------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+
Helaas heeft MySQL geen FULL JOIN
. Je kunt het in andere RDBMS'en proberen, en het zal tonen:
+------+--------+------+-------+
| 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 |
+------+--------+------+-------+