Из приведенных здесь ответов можно сделать вывод, что 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
изменений меняет семантическое поведение не в этом наборе на что-то еще?