可以从这里的答案得出结论,即NOT IN (subquery)
不能正确处理空值,应避免使用NOT EXISTS
。但是,这样的结论可能为时过早。在以下情况下,应归功于Chris Date(数据库编程和设计,第2卷第9期,1989年9月),它可以NOT IN
正确处理null并返回正确的结果,而不是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
在Standard 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
语言中没有的语义行为转换为一系列变化?