排除Null值会在子查询中返回0行

时间:2013-01-14 17:00:45

标签: sql tsql subquery isnull

我正在尝试清理SQL Server中的一些数据,并在两个表之间添加一个外键。

我想删除其中一个表中的大量孤立行。我不知道为什么以下查询将在MS SQL服务器中返回0行。

- 此查询不返回任何行

  

从tbl_A中选择*,其中ID不在(从tbl_B中选择不同的ID)   )

当我在子查询中包含IS NOT NULL时,我得到了我期望的结果。

- 返回包含tbl_A但不在tbl_B中的所有记录的行

  

从tbl_A中选择*,其中ID不在(从tbl_B中选择不同的ID)   其中ID不为空)

ID列可以为空,并且包含空值。如果我只运行子查询,我得到完全相同的结果,除了第一个查询返回一个额外的NULL行按预期。

5 个答案:

答案 0 :(得分:2)

这是NOT IN子查询的预期行为。当子查询返回单个nullNOT IN时,将不匹配任何行。

如果您不想专门进行null检查,那么您需要使用NOT EXISTS

select * 
from tbl_A A
where not exists (select distinct ID 
                  from tbl_B b
                  where a.id = b.id)

至于NOT IN导致问题的原因,以下是一些讨论它的帖子:

NOT IN vs. NOT EXISTS vs. LEFT JOIN / IS NULL

NOT EXISTS vs NOT IN

What's the difference between NOT EXISTS vs. NOT IN vs. LEFT JOIN WHERE IS NULL?

答案 1 :(得分:1)

使用equals(=)匹配NULL将从逻辑角度返回NULL或UNKNOWN,而不是true / false。例如。请参阅http://msdn.microsoft.com/en-us/library/aa196339(v=sql.80).aspx进行讨论。

如果你想在表A中找到NULL值,其中表B中没有NULL(如果B是“父”,A是你想要的“外键”关系中的“子”)那么你会需要第二个声明,如下所示。此外,我建议使用表前缀或别名限定ID字段,因为两个表中的字段名称相同。最后,我不建议将NULL值作为键。但无论如何:

select * from tbl_A as A where (A.ID not in ( select distinct B.ID from tbl_B as B ))
  or (A.ID is NULL and not exists(select * from tbl_B as B where B.ID is null))

答案 2 :(得分:0)

您可能已关闭ANSI NULL。这会比较空值,因此null = null将返回true。

使用

添加第一个查询
SET ANSI_NULLS ON
GO

答案 3 :(得分:0)

问题在于空值的不可比性。如果你问“不在”并且子查询中有空值,它就不能说任何东西肯定都没有,因为它将这些空值视为“未知”,因此答案在三值逻辑中总是“未知” SQL使用。

现在当然假设您已经打开ANSI_NULLS(这是默认设置)如果你关闭它然后突然NULLS变得可比,它会给你结果,可能是你期望的结果。

答案 4 :(得分:0)

如果ID永远不会消极,您可以考虑以下内容:

select *
from tbl_A
where coalesce(ID, -1) not in ( select distinct coalesce(ID, -1) from tbl_B )

(或者如果id是一个字符串,请使用第coalesce(id, '<null>')行)。

这可能并不适用于所有情况,但它在编码级别上具有简单的优点。