可以从这里的答案中得出结论,这些答案NOT IN (subquery)
没有正确处理空值,应该避免使用NOT EXISTS
. 然而,这样的结论可能为时过早。在以下场景中,归功于 Chris Date(数据库编程和设计,第 2 卷第 9 期,1989 年 9 月),它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
references 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;