我正在尝试清理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行按预期。
答案 0 :(得分:2)
这是NOT IN
子查询的预期行为。当子查询返回单个null
值NOT 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
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>')
行)。
这可能并不适用于所有情况,但它在编码级别上具有简单的优点。