Oracle
 sql >> Database >  >> RDS >> Oracle

Utilizzo di IS NULL o IS NOT NULL su condizioni di unione - Domanda di teoria

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'è pidchild.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   |
+------+--------+------+-------+