Esempio con le tabelle A e 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
Se vuoi trovare genitori e figli, fai un INNER JOIN
:
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent INNER JOIN child
ON parent.id = child.pid
Il risultato è che ogni corrispondenza di un parent
id
di dalla tabella di sinistra e un child
pid
di dalla seconda tabella apparirà come riga nel risultato:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
+----+--------+------+-------+
Ora, quanto sopra non mostra i genitori senza figli (perché i loro ID non hanno una corrispondenza negli ID dei bambini, quindi cosa fai? Fai invece un join esterno. Esistono tre tipi di join esterni, il sinistro, il destro e il join esterno completo. Abbiamo bisogno di quello sinistro perché vogliamo le righe "extra" dalla tabella di sinistra (genitore):
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent LEFT JOIN child
ON parent.id = child.pid
Il risultato è che oltre alle partite precedenti, vengono mostrati anche tutti i genitori che non hanno una corrispondenza (leggi:non hanno un figlio):
+----+--------+------+-------+
| 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 |
+----+--------+------+-------+
Dove sono finiti tutti quei NULLs
vieni da? Bene, MySQL (o qualsiasi altro RDBMS che potresti usare) non saprà cosa mettere lì perché questi genitori non hanno corrispondenze (kid), quindi non c'è pid
né child.name
da abbinare a quei genitori. Quindi, mette questo speciale non-valore chiamato NULLs
.
Il punto è che questi NULLs
vengono creati (nel set di risultati) durante il LEFT OUTER JOIN
.
Quindi, se vogliamo mostrare solo i genitori che NON hanno un figlio, possiamo aggiungere un WHERE child.pid IS NULL
al LEFT JOIN
sopra. Il WHERE
la clausola viene valutata (selezionata) dopo il JOIN
è fatta. Quindi, dal risultato sopra è chiaro che solo le ultime tre righe contengono pid
è NULL verrà mostrato:
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
Risultato:
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 2 | Bill | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Ora, cosa succede se spostiamo quel IS NULL
controlla da WHERE
al collegamento ON
clausola?
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 questo caso il database tenta di trovare le righe delle due tabelle che soddisfano queste condizioni. Ovvero, righe in cui parent.id = child.pid
E child.pid IN NULL
. Ma non riesce a trovare nessuna corrispondenza del genere perché nessun child.pid
può essere uguale a qualcosa (1, 2, 3, 4 o 5) ed essere NULL allo stesso tempo!
Quindi, la condizione:
ON parent.id = child.pid
AND child.pid IS NULL
equivale a:
ON 1 = 0
che è sempre False
.
Quindi, perché restituisce TUTTE le righe dalla tabella di sinistra? Perché è un UNISCITI A SINISTRA! E i join sinistro restituiscono righe che corrispondono (nessuna in questo caso) e anche righe della tabella di sinistra che non corrispondono il segno di spunta (tutto in questo caso ):
+----+--------+------+-------+
| id | parent | pid | child |
+----+--------+------+-------+
| 1 | Alex | NULL | NULL |
| 2 | Bill | NULL | NULL |
| 3 | Cath | NULL | NULL |
| 4 | Dale | NULL | NULL |
| 5 | Evan | NULL | NULL |
+----+--------+------+-------+
Spero che la spiegazione di cui sopra sia chiara.
Nota a margine (non direttamente correlata alla tua domanda):perché diavolo non Pan
non apparire in nessuno dei nostri JOIN? Perché il suo pid
è NULLs
e NULL nella logica (non comune) di SQL non è uguale a nulla, quindi non può corrispondere a nessuno degli ID padre (che sono 1,2,3,4 e 5). Anche se ci fosse un NULL lì, non corrisponderebbe comunque perché NULLs
non è uguale a niente, nemmeno NULLs
stesso (è una logica molto strana, in effetti!). Ecco perché utilizziamo il controllo speciale IS NULL
e non un = NULL
controlla.
Quindi, Pan
mostrati se facciamo un RIGHT JOIN
? Si lo farà! Perché un RIGHT JOIN mostrerà tutti i risultati che corrispondono (il primo INNER JOIN che abbiamo fatto) più tutte le righe della tabella RIGHT che non corrispondono (che nel nostro caso è uno, il (NULL, 'Pan')
riga.
SELECT id, parent.name AS parent
, pid, child.name AS child
FROM
parent RIGHT JOIN child
ON parent.id = child.pid
Risultato:
+------+--------+------+-------+
| id | parent | pid | child |
+---------------+------+-------+
| 1 | Alex | 1 | Kate |
| 1 | Alex | 1 | Lia |
| 3 | Cath | 3 | Mary |
| NULL | NULL | NULL | Pan |
+------+--------+------+-------+
Sfortunatamente, MySQL non ha FULL JOIN
. Puoi provarlo in altri RDBMS e mostrerà:
+------+--------+------+-------+
| 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 |
+------+--------+------+-------+