5

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

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

谢谢!

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

4 回答 4

8

这是设计使然。如果匹配失败并且集合包含 NULL,则结果为 NULL,如 SQL 标准所指定。

'1' IN ('1', '3') => 真
'2' IN ('1', '3') => 假
'1' IN ('1', NULL) => true
'2' IN ('1', NULL) => NULL

'1' 不在 ('1', '3') => false
'2' 不在 ('1', '3') => true
'1' 不在 ('1', NULL) => false
'2' 不在 ('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. 然后你可以使用三值逻辑的规则:

true AND NULL => NULL
false AND NULL => false
于 2010-12-27T14:31:56.327 回答
3

是的,这就是它的设计方式。LEFT JOIN在执行 a或 a之间还有许多其他考虑因素NOT IN。您应该看到此链接以很好地解释此行为。

于 2010-12-27T14:43:07.200 回答
0

这是 ANSI 委员会认为必须这样做的方式。

您可以在查询之前使用

set ansi_defaults OFF

你会得到你期望的结果。

由于 SQL-Server 7.0 微软对遵循 ansi 标准相当严格。

编辑:

不要与默认值作斗争。你最终会放弃。

于 2010-12-27T14:39:15.003 回答
0

Mark 解释了该行为的根本原因。它可以通过多种方式解决,- LEFT JOIN,通过从 where 子句或 select 子句中过滤掉内部查询中的 NULL 值,使用相关的子查询 - 仅举几例。

以下三篇短文是关于同一主题的案例研究:- NOT IN 子查询返回零行- 问题,NOT IN 子查询返回零行 -根本原因NOT IN 子查询返回零行 - 解决方法

于 2014-03-08T15:01:23.557 回答