Из ответов здесь можно сделать вывод, что NOT IN (subquery)
неправильно обрабатывает нули и его следует избегать в пользу NOT EXISTS
. Однако такой вывод может быть преждевременным. В следующем сценарии, зачисленном в Chris Date (Database Programming and Design, Vol 2 No 9, September 1989), NOT IN
обрабатывает нули правильно и возвращает правильный результат, а не NOT EXISTS
.
Рассмотрим таблицу sp
для представления поставщиков (sno
), которые, как известно, поставляют детали (pno
) в количестве (qty
). В таблице в настоящее время хранятся следующие значения:
VALUES ('S1', 'P1', NULL),
('S2', 'P1', 200),
('S3', 'P1', 1000)
Обратите внимание, что количество является нулевым, т. Е. Для записи факта, что поставщик, как известно, предоставляет детали, даже если в каком количестве неизвестно.
Задача состоит в том, чтобы найти поставщиков, которые известны номером запасной части «P1», но не в количествах 1000.
Следующие правила используют NOT IN
для правильной идентификации поставщика S2, only:
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'
);
Однако в нижеприведенном запросе используется одна и та же общая структура, но с NOT EXISTS
, но неверно включает в себя поставщик S1 в результате (т.е. для которого значение равно null):
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
);
Таким образом, NOT EXISTS
не является серебряной пулей, она, возможно, появилась!
Конечно, источником проблемы является наличие нулей, поэтому «реальным» решением является устранение эти нули.
Это может быть достигнуто (среди других возможных конструкций) с использованием двух таблиц:
sp
поставщиков, которые, как известно, поставляют детали spq
поставщиков, которые, как известно, поставляют детали в известных количествах , отметив, что, вероятно, должно быть ограничение внешнего ключа, где spq
ссылается sp
.
Результат может тогда быть получена с использованием реляционного оператора «минус» (являющегося ключевым словом EXCEPT
в стандартном SQL), например
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;