On peut conclure des réponses ici qui NOT IN (subquery)
ne traitent pas correctement les valeurs nulles et doivent être évitées en faveur de NOT EXISTS
. Cependant, une telle conclusion peut être prématurée. Dans le scénario suivant, attribué à Chris Date (Database Programming and Design, Vol 2 No 9, septembre 1989), c'est NOT IN
qu'il gère correctement les valeurs nulles et renvoie le résultat correct, plutôt que NOT EXISTS
.
Considérons un tableau sp
pour représenter les fournisseurs ( sno
) qui sont connus pour fournir des pièces ( pno
) en quantité ( qty
). Le tableau contient actuellement les valeurs suivantes:
VALUES ('S1', 'P1', NULL),
('S2', 'P1', 200),
('S3', 'P1', 1000)
Notez que la quantité peut être annulée, c'est-à-dire pour pouvoir enregistrer le fait qu'un fournisseur est connu pour fournir des pièces même s'il n'est pas connu en quelle quantité.
La tâche consiste à trouver les fournisseurs connus qui fournissent le numéro de pièce «P1» mais pas en quantités de 1000.
Les utilisations suivantes permettent NOT IN
d'identifier correctement le fournisseur «S2» uniquement:
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1', NULL ),
( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT DISTINCT spx.sno
FROM sp spx
WHERE spx.pno = 'P1'
AND 1000 NOT IN (
SELECT spy.qty
FROM sp spy
WHERE spy.sno = spx.sno
AND spy.pno = 'P1'
);
Cependant, la requête ci-dessous utilise la même structure générale mais avec NOT EXISTS
mais inclut incorrectement le fournisseur «S1» dans le résultat (c'est-à-dire pour lequel la quantité est nulle):
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1', NULL ),
( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT DISTINCT spx.sno
FROM sp spx
WHERE spx.pno = 'P1'
AND NOT EXISTS (
SELECT *
FROM sp spy
WHERE spy.sno = spx.sno
AND spy.pno = 'P1'
AND spy.qty = 1000
);
Ce NOT EXISTS
n'est donc pas la balle d'argent qu'elle a pu apparaître!
Bien sûr, la source du problème est la présence de nulls, donc la «vraie» solution est d'éliminer ces nulls.
Ceci peut être réalisé (entre autres conceptions possibles) en utilisant deux tableaux:
sp
fournisseurs connus pour fournir des pièces
spq
fournisseurs connus pour fournir des pièces en quantités connues
notant qu'il devrait probablement y avoir une contrainte de clé étrangère où les spq
références sp
.
Le résultat peut ensuite être obtenu en utilisant l'opérateur relationnel «moins» (qui est le EXCEPT
mot clé dans SQL standard), par exemple
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1' ),
( 'S2', 'P1' ),
( 'S3', 'P1' ) )
AS T ( sno, pno )
),
spq AS
( SELECT *
FROM ( VALUES ( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT sno
FROM spq
WHERE pno = 'P1'
EXCEPT
SELECT sno
FROM spq
WHERE pno = 'P1'
AND qty = 1000;
NOT IN
en une série de<> and
changements modifie le comportement sémantique de pas dans cet ensemble à autre chose?