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

Sélection NOT IN avec des valeurs NULL

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.