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