Из приведенных здесь ответов можно сделать вывод, что 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;