Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Usando IS NULL ou IS NOT NULL em condições de junção - questão teórica


Exemplo com as tabelas 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 você quiser encontrar pais e filhos, faça um INNER JOIN :
SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  INNER JOIN  child
  ON   parent.id     =    child.pid

O resultado é que cada correspondência de um pai 's id da tabela à esquerda e um filho 's pid da segunda tabela aparecerá como uma linha no resultado:
+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  1 | Alex   |   1  | Kate  |
|  1 | Alex   |   1  | Lia   |
|  3 | Cath   |   3  | Mary  |
+----+--------+------+-------+

Agora, o exemplo acima não mostra os pais sem filhos (porque seus ids não têm uma correspondência nos ids dos filhos, então o que você faz? Você faz uma junção externa. Existem três tipos de junções externas, a esquerda, a direita e a junção externa completa. Precisamos da esquerda, pois queremos as linhas "extras" da tabela esquerda (pai):
SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  LEFT JOIN  child
  ON   parent.id    =    child.pid

O resultado é que além das partidas anteriores, todos os pais que não têm uma partida (leia-se:não tem filho) também são mostrados:
+----+--------+------+-------+
| 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  |
+----+--------+------+-------+

Onde foram parar todos aqueles NULL vem de onde? Bem, o MySQL (ou qualquer outro RDBMS que você possa usar) não saberá o que colocar lá, pois esses pais não têm correspondência (criança), então não há pid nem child.name para combinar com esses pais. Então, ele coloca esse não valor especial chamado NULL .

Meu ponto é que esses NULLs são criados (no conjunto de resultados) durante o LEFT OUTER JOIN .

Portanto, se quisermos mostrar apenas os pais que NÃO têm filhos, podemos adicionar um WHERE child.pid IS NULL para o LEFT JOIN acima de. O ONDE cláusula é avaliada (marcada) após o JOIN é feito. Portanto, fica claro pelo resultado acima que apenas as três últimas linhas em que o pid é NULL será mostrado:
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

Resultado:
+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  2 | Bill   | NULL | NULL  |
|  4 | Dale   | NULL | NULL  |
|  5 | Evan   | NULL | NULL  |
+----+--------+------+-------+

Agora, o que acontece se movermos IS NULL verifique em ONDE para a entrada ON cláusula?
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

Nesse caso, o banco de dados tenta encontrar linhas das duas tabelas que correspondam a essas condições. Ou seja, linhas onde parent.id =child.pid E child.pid IN NULL . Mas ele pode encontrar nenhuma correspondência porque não há child.pid pode ser igual a algo (1, 2, 3, 4 ou 5) e ser NULL ao mesmo tempo!

Então, a condição:
ON   parent.id    =    child.pid
AND  child.pid IS NULL

é equivalente a:
ON   1 = 0

que é sempre Falso .

Então, por que ele retorna TODAS as linhas da tabela à esquerda? Porque é um LEFT JOIN! E as junções à esquerda retornam linhas que correspondem (nenhuma neste caso) e também linhas da tabela esquerda que não correspondem o cheque (todos neste 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  |
+----+--------+------+-------+

Espero que a explicação acima seja clara.

Nota lateral (não diretamente relacionada à sua pergunta):Por que diabos não Pan não apareceu em nenhum dos nossos JOINs? Porque seu pid é NULL e NULL na lógica (não comum) do SQL não é igual a nada, então não pode corresponder a nenhum dos ids pai (que são 1,2,3,4 e 5). Mesmo que houvesse um NULL lá, ele ainda não corresponderia porque NULL não é igual a nada, nem mesmo NULL em si (é uma lógica muito estranha, de fato!). É por isso que usamos a verificação especial IS NULL e não um =NULL Verifica.

Então, Pan aparecer se fizermos um RIGHT JOIN ? Sim vai! Porque um RIGHT JOIN mostrará todos os resultados que correspondem (o primeiro INNER JOIN que fizemos) mais todas as linhas da tabela RIGHT que não correspondem (que no nosso caso é um, o (NULL, 'Pan') fileira.
SELECT id,  parent.name AS parent
     , pid, child.name  AS child

FROM
        parent  RIGHT JOIN  child
  ON   parent.id     =    child.pid

Resultado:
+------+--------+------+-------+
| id   | parent | pid  | child | 
+---------------+------+-------+
|   1  | Alex   |   1  | Kate  |
|   1  | Alex   |   1  | Lia   |
|   3  | Cath   |   3  | Mary  |
| NULL | NULL   | NULL | Pan   |
+------+--------+------+-------+

Infelizmente, o MySQL não tem FULL JOIN . Você pode experimentá-lo em outros RDBMSs e ele mostrará:
+------+--------+------+-------+
|  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   |
+------+--------+------+-------+