Si può concludere dalle risposte qui che NOT IN (subquery)
non gestiscono correttamente i null e dovrebbero essere evitate a favore NOT EXISTS
. Tuttavia, tale conclusione può essere prematura. Nel seguente scenario, accreditato a Chris Date (Programmazione e progettazione del database, volume 2 n. 9, settembre 1989), è NOT IN
che gestisce correttamente i null e restituisce il risultato corretto, anziché NOT EXISTS
.
Si consideri una tabella sp
per rappresentare i fornitori ( sno
) che sono noti per fornire parti ( pno
) in quantità ( qty
). La tabella contiene attualmente i seguenti valori:
VALUES ('S1', 'P1', NULL),
('S2', 'P1', 200),
('S3', 'P1', 1000)
Si noti che la quantità è nullable cioè per essere in grado di registrare il fatto che un fornitore è noto per fornire parti anche se non è noto in quale quantità.
Il compito è quello di trovare i fornitori che sono noti fornire il numero di parte 'P1' ma non in quantità di 1000.
I seguenti usi NOT IN
per identificare correttamente solo il fornitore 'S2':
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'
);
Tuttavia, la query seguente utilizza la stessa struttura generale ma con NOT EXISTS
il risultato include erroneamente il fornitore "S1" nel risultato (ovvero per cui la quantità è nulla):
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
);
Quindi NOT EXISTS
non è il proiettile d'argento che potrebbe essere apparso!
Naturalmente, la fonte del problema è la presenza di null, quindi la soluzione "reale" è quella di eliminare quei null.
Ciò può essere ottenuto (tra gli altri possibili progetti) utilizzando due tabelle:
sp
fornitori noti per la fornitura di parti
spq
fornitori noti per la fornitura di parti in quantità note
notando che probabilmente ci dovrebbe essere un vincolo di chiave esterna in cui i spq
riferimenti sp
.
Il risultato può quindi essere ottenuto usando l'operatore relazionale 'meno' (essendo la EXCEPT
parola chiave in SQL standard) ad es
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
in una serie di<> and
cambiamenti il comportamento semantico di non in questo insieme in qualcos'altro?