Звідси можна зробити висновок, що 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
змінює смислову поведінку не в цій множині на щось інше?