D'abord un peu de théorie :Null (SQL)
Les parties les plus importantes pour nous du lien ci-dessus :
Comparaisons avec NULL et la logique à trois valeurs (3VL)
Étant donné que Null n'est membre d'aucun domaine de données, il n'est pas considéré comme une "valeur", mais plutôt comme un marqueur (ou un espace réservé) indiquant l'absence de valeur. Pour cette raison, les comparaisons avec Null ne peuvent jamais aboutir à Vrai ou Faux, mais toujours à un troisième résultat logique, Inconnu.[8] Le résultat logique de l'expression ci-dessous, qui compare la valeur 10 à Null, est Unknown :
SELECT 10 = NULL -- Results in Unknown
de sorte que les deux comparaisons :x = NULL
et x <> NULL
est évalué à NULL(inconnu).
SQL implémente trois résultats logiques, donc les implémentations SQL doivent fournir une logique spécialisée à trois valeurs (3VL). Les règles régissant la logique à trois valeurs SQL sont présentées dans les tableaux ci-dessous (p et q représentent des états logiques) "[9] Les tables de vérité que SQL utilise pour AND, OR et NOT correspondent à un fragment commun de la logique à trois valeurs de Kleene et Łukasiewicz ( qui diffèrent dans leur définition de l'implication, cependant SQL ne définit pas une telle opération).
+---------+-------------+-------------+-------------+-----------+--------+ | p | q | p OR q | p AND q | p = q |p != q | +---------+-------------+-------------+-------------+-----------+--------+ | True | True | True | True | True | False | | True | False | True | False | False | True | | True | Unknown | True | Unknown | Unknown | Unknown| | False | True | True | False | False | True | | False | False | False | False | True | False | | False | Unknown | Unknown | False | Unknown | Unknown| | Unknown | True | True | Unknown | Unknown | Unknown| | Unknown | False | Unknown | False | Unknown | Unknown| | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown| +---------+-------------+-------------+-------------+-----------+--------+
Effet de Inconnu dans les clauses WHERE
La logique à trois valeurs SQL est rencontrée dans le langage de manipulation de données (DML) dans les prédicats de comparaison des instructions et des requêtes DML. La clause WHERE oblige l'instruction DML à agir uniquement sur les lignes pour lesquelles le prédicat est évalué à True.
Donc en bref :la clause WHERE traite NULL comme FALSE
Considérons maintenant un cas plus simple :
SELECT * FROM T1;
| X |
|--------|
| 1 |
| (null) |
et une requête :
SELECT * FROM t1 WHERE x IN (1, NULL);
La requête ci-dessus est un raccourci vers celle-ci :
SELECT * FROM t1
WHERE x = 1
OR x = NULL
Pour la deuxième ligne du tableau t
( x =NULL) cette condition ressemble à :
WHERE NULL = 1
OR NULL = NULL
donc cette condition pour la ligne x=NULL
est évalué à NULL car NULL=1
est NULL, NULL=NULL
est NULL, et NULL OR NULL
est également NULL (veuillez consulter le tableau 3VL ci-dessus).
Considérons maintenant un cas plus curieux :
SELECT * FROM t1 WHERE x NOT IN (1, NULL);
Cette clause x NOT IN (1, NULL)
est équivalent à NOT ( x IN (1, NULL) )
il est donc également équivalent à :
NOT (
x = 1
OR
x = NULL
)
et selon les lois de De Morgan cela équivaut à :
NOT ( x = 1 ) AND NOT ( x = NULL )
et (si on remplace NOT x = y
avec x <> y
) c'est aussi équivalent à :
x <> 1 AND x <> NULL
Veuillez regarder attentivement la dernière condition :
WHERE
x <> 1 AND x <> NULL
Nous savons que x <> NULL
vaut toujours NULL. Nous savons également à partir de la table 3VL ci-dessus, que les deux true AND NULL
est NULL et false AND NULL
est évaluée à FALSE, donc la condition entière est toujours évaluée à FALSE ou NULL, mais elle n'est jamais évaluée à TRUE.
Par conséquent, une requête avec cette condition :
SELECT .....
WHERE x NOT IN ( NULL, whatever)
renvoie toujours un jeu de résultats vide
Et maintenant votre requête, qui est aussi curieuse :
SELECT * FROM t1
WHERE (id, val) NOT IN (select id, val from data2);
qui peut être réécrit (en utilisant des valeurs constantes) en :
SELECT * FROM t1
WHERE (id, val) NOT IN (
(1, null),
(2, 2 )
)
Cette requête utilise ce qu'on appelle l'expression de valeur de ligne
Fondamentalement, une condition utilisant l'expression de valeur de ligne comme celle-ci
(a, b) = (x, y)
est équivalent à celui-ci :
a = x AND b = y
donc la requête ci-dessus peut être réécrite dans celle-ci :
SELECT * FROM t1
WHERE NOT (
id = 1 AND val = NULL
OR
id = 2 AND val = 2
)
Selon les lois de De Morgan, cela est identique à :
SELECT * FROM t1
WHERE
NOT ( id = 1 AND val = NULL )
AND
NOT ( id = 2 AND val = 2 )
et suite à :
SELECT * FROM t1
WHERE
( id <> 1 OR val <> NULL )
AND
( id <> 2 OR val <> 2 )
Depuis la première partie ( id <> 1 OR val <> NULL )
de la condition prend la valeur true uniquement dans le cas où id <> 1
(veuillez consulter le tableau 3VL ci-dessus), cette condition peut être simplifiée en :
SELECT * FROM t1
WHERE
( id <> 1 )
AND
( id <> 2 OR val <> 2 )
et plus loin (selon les lois de De Morgan) dans :
SELECT * FROM t1
WHERE
id <> 1 AND id <> 2
OR
id <> 1 AND val <> 2
donc ni (1,1)
ni (2,2)
à partir de la source data1
respecter ces conditions.