Można wywnioskować z odpowiedzi, które NOT IN (subquery)
nie obsługują poprawnie wartości zerowych i należy ich unikać na korzyść NOT EXISTS
. Taki wniosek może być jednak przedwczesny. W poniższym scenariuszu, przypisanym Chrisowi Date (programowanie i projektowanie baz danych, tom 2 nr 9, wrzesień 1989), oznacza to, NOT IN
że poprawnie obsługuje wartości zerowe i zwraca poprawny wynik NOT EXISTS
.
Rozważ tabelę sp
przedstawiającą dostawców ( sno
), o których wiadomo, że dostarczają części ( pno
) w ilości ( qty
). Tabela zawiera obecnie następujące wartości:
VALUES ('S1', 'P1', NULL),
('S2', 'P1', 200),
('S3', 'P1', 1000)
Należy pamiętać, że ilość jest zerowa, tj. Aby móc odnotować fakt, że dostawca jest znany z dostarczania części, nawet jeśli nie wiadomo, w jakiej ilości.
Zadanie polega na znalezieniu dostawców, którzy są znanymi dostawcami o numerze katalogowym „P1”, ale nie w ilości 1000.
Następujące zastosowania NOT IN
do prawidłowej identyfikacji wyłącznie dostawcy „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'
);
Jednak poniższe zapytanie wykorzystuje tę samą ogólną strukturę, ale z wynikiem, NOT EXISTS
ale niepoprawnie zawiera w wynikach dostawcę „S1” (tj. Dla którego ilość jest zerowa):
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
);
Więc NOT EXISTS
nie jest to srebrna kula, którą mogła się pojawić!
Oczywiście źródłem problemu jest obecność wartości zerowych, dlatego „prawdziwym” rozwiązaniem jest wyeliminowanie tych wartości zerowych.
Można to osiągnąć (między innymi możliwymi projektami) za pomocą dwóch tabel:
sp
dostawcy znani z dostarczania części
spq
dostawcy znani z dostarczania części w znanych ilościach
Należy zauważyć, że prawdopodobnie ograniczenie klucz obcy gdzie spq
referencjesp
.
Wynik można następnie uzyskać za pomocą operatora relacyjnego „minus” (będącego EXCEPT
słowem kluczowym w Standard SQL) np
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
w szereg<> and
zmian zachowania semantycznego nie w tym zestawie na coś innego?