我遇到了这个有趣的行为。我看到左连接是要走的路,但仍然希望将其清除。是设计中的错误还是行为?有什么解释吗?
当我从左表中选择记录时,右表上子查询的结果中不存在值,如果子查询结果为空,则不返回预期的“缺失”记录。我期望将这个查询写成两种方法是等效的。
谢谢!
declare @left table (id int not null primary key identity(1,1), ref int null)
declare @right table (id int not null primary key identity(1,1), ref int null)
insert @left (ref) values (1)
insert @left (ref) values (2)
insert @right (ref) values (1)
insert @right (ref) values (null)
print 'unexpected empty resultset:'
select * from @left
where ref not in (select ref from @right)
print 'expected result - ref 2:'
select * from @left
where ref not in (select ref from @right where ref is not null)
print 'expected result - ref 2:'
select l.* from @left l
left join @right r on r.ref = l.ref
where r.id is null
print @@version
给出:
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
unexpected empty resultset:
id ref
----------- -----------
(0 row(s) affected)
expected result - ref 2:
id ref
----------- -----------
2 2
(1 row(s) affected)
expected result - ref 2:
id ref
----------- -----------
2 2
(1 row(s) affected)
Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64)
Apr 2 2010 15:48:46
Copyright (c) Microsoft Corporation
Standard Edition (64-bit) on Windows NT 6.0 <X64> (Build 6002: Service Pack 2) (Hypervisor)
答案 0 :(得分:8)
这是设计的。如果匹配失败并且set包含NULL,则结果为NULL,如SQL标准所指定。
'1' IN ('1', '3') => true '2' IN ('1', '3') => false '1' IN ('1', NULL) => true '2' IN ('1', NULL) => NULL '1' NOT IN ('1', '3') => false '2' NOT IN ('1', '3') => true '1' NOT IN ('1', NULL) => false '2' NOT IN ('1', NULL) => NULL
非正式地说,这背后的逻辑是NULL可以被认为是一个未知值。例如,未知值是什么并不重要 - “1”显然在集合中,因此结果为真。
'1' IN ('1', NULL) => true
在下面的示例中,我们无法确定“2”是否在集合中,但由于我们不知道所有值,因此我们也无法确定不是在集合中。所以结果是NULL。
'2' IN ('1', NULL) => NULL
另一种看待它的方法是将x NOT IN (Y, Z)
重写为X <> Y AND X <> Z
。然后,您可以使用three-valued logic的规则:
true AND NULL => NULL
false AND NULL => false
答案 1 :(得分:3)
是的,这就是它的设计方式。在执行LEFT JOIN
或NOT IN
之间还有许多其他注意事项。你应该看到这个link对这种行为有一个非常好的解释。
答案 2 :(得分:0)
这是ANSI委员会认为必须完成的方式。
您可以在
之前查询set ansi_defaults OFF
你会得到你期望的结果。
由于SQL-Server 7.0,Microsoft对遵循ansi标准非常严格。
修改强>
不要与默认值作斗争。你最后会放弃。
答案 3 :(得分:0)
Mark解释了这种行为的根本原因。它可以通过多种方式解决, - LEFT JOIN,通过从where子句或select子句中过滤掉来自内部查询的NULL值,使用共同相关的子查询 - 仅举几例。
以下三篇简短帖子是针对同一主题的案例研究: - NOT IN Subquery return zero rows -Issue,NOT IN Subquery return zero rows -Root Cause,NOT IN Subquery return zero rows -Workarounds