如果子查询包含NULL,则使用“IN”子查询的SQL select不返回任何记录

时间:2010-12-27 14:29:14

标签: sql null left-join in-subquery

我遇到了这个有趣的行为。我看到左连接是要走的路,但仍然希望将其清除。是设计中的错误还是行为?有什么解释吗?

当我从左表中选择记录时,右表上子查询的结果中不存在值,如果子查询结果为空,则不返回预期的“缺失”记录。我期望将这个查询写成两种方法是等效的。

谢谢!

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)

4 个答案:

答案 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 JOINNOT 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 -IssueNOT IN Subquery return zero rows -Root CauseNOT IN Subquery return zero rows -Workarounds