Oracle
 sql >> Base de données >  >> RDS >> Oracle

Utilisation de IS NULL ou IS NOT NULL sur les conditions de jointure - Question théorique

Exemple avec les tableaux A et 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  

Si vous voulez trouver des parents et leurs enfants, vous faites un INNER JOIN :

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

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

Le résultat est que chaque correspondance d'un parent L'id de du tableau de gauche et un child le pid de du deuxième tableau apparaîtra sous forme de ligne dans le résultat :

+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  1 | Alex   |   1  | Kate  |
|  1 | Alex   |   1  | Lia   |
|  3 | Cath   |   3  | Mary  |
+----+--------+------+-------+

Maintenant, ce qui précède ne montre pas les parents sans enfants (parce que leurs identifiants n'ont pas de correspondance dans les identifiants des enfants, alors que faites-vous ? Vous faites une jointure externe à la place. Il existe trois types de jointures externes, la gauche, la droite et la jointure externe complète. Nous avons besoin de celle de gauche car nous voulons les lignes "supplémentaires" de la table de gauche (parent) :

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

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

Le résultat est qu'en plus des correspondances précédentes, tous les parents qui n'ont pas de correspondance (lire :n'ont pas d'enfant) sont également affichés :

+----+--------+------+-------+
| 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  |
+----+--------+------+-------+

D'où viennent tous ces NULL viens de? Eh bien, MySQL (ou tout autre SGBDR que vous pourriez utiliser) ne saura pas quoi y mettre car ces parents n'ont pas de correspondance (enfant), donc il n'y a pas de pid ni child.name pour correspondre avec ces parents. Donc, il met cette non-valeur spéciale appelée NULL .

Mon point est que ces NULLs sont créés (dans le jeu de résultats) lors de la LEFT OUTER JOIN .

Donc, si nous voulons montrer uniquement les parents qui n'ont PAS d'enfant, nous pouvons ajouter un WHERE child.pid IS NULL à la LEFT JOIN au dessus de. Le WHERE la clause est évaluée (vérifiée) après le JOIN est fait. Ainsi, il ressort clairement du résultat ci-dessus que seules les trois dernières lignes où le pid est NULL sera affiché :

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

Résultat :

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

Maintenant, que se passe-t-il si nous déplaçons ce IS NULL vérifier à partir de WHERE à l'adhésion ON clause ?

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

Dans ce cas, la base de données essaie de trouver les lignes des deux tables qui correspondent à ces conditions. Autrement dit, les lignes où parent.id = child.pid ET child.pid IN NULL . Mais il ne peut trouver aucune correspondance de ce type car pas de child.pid peut être égal à quelque chose (1, 2, 3, 4 ou 5) et être NULL en même temps !

Donc, la condition :

ON   parent.id    =    child.pid
AND  child.pid IS NULL

est équivalent à :

ON   1 = 0

qui est toujours False .

Alors, pourquoi renvoie-t-il TOUTES les lignes de la table de gauche ? Parce que c'est une JOINTURE GAUCHE ! Et les jointures gauches renvoient les lignes qui correspondent (aucune dans ce cas) ainsi que les lignes du tableau de gauche qui ne correspondent pas le chèque (tous dans ce cas ):

+----+--------+------+-------+
| id | parent | pid  | child | 
+----+--------+------+-------+
|  1 | Alex   | NULL | NULL  |
|  2 | Bill   | NULL | NULL  |
|  3 | Cath   | NULL | NULL  |
|  4 | Dale   | NULL | NULL  |
|  5 | Evan   | NULL | NULL  |
+----+--------+------+-------+

J'espère que l'explication ci-dessus est claire.

Sidenote (pas directement liée à votre question) :Pourquoi diable ne fait-il pas Pan apparaître dans aucun de nos JOINs ? Parce que son pid est NULL et NULL dans la logique (non commune) de SQL n'est pas égal à quoi que ce soit, il ne peut donc correspondre à aucun des identifiants parents (qui sont 1, 2, 3, 4 et 5). Même s'il y avait un NULL ici, il ne correspondrait toujours pas car NULL n'est égal à rien, pas même NULL lui-même (c'est une logique très étrange, en effet !). C'est pourquoi nous utilisons la vérification spéciale IS NULL et non un = NULL vérifier.

Donc, va Pan apparaît si nous faisons un RIGHT JOIN ? Oui, il sera! Parce qu'un RIGHT JOIN affichera tous les résultats qui correspondent (le premier INNER JOIN que nous avons fait) plus toutes les lignes de la table RIGHT qui ne correspondent pas (qui dans notre cas est un, le (NULL, 'Pan') rangée.

SELECT id,  parent.name AS parent
     , pid, child.name  AS child

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

Résultat :

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

Malheureusement, MySQL n'a pas FULL JOIN . Vous pouvez l'essayer dans d'autres SGBDR, et il affichera :

+------+--------+------+-------+
|  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   |
+------+--------+------+-------+