【问题标题】:SQL Server: Why does comparison null=value return true for NOT IN?SQL Server:为什么比较 null=value 对于 NOT IN 返回 true?
【发布时间】:2010-10-13 14:22:09
【问题描述】:

为什么valuenull 的比较返回false,除非使用NOT IN,它返回true?


给定一个查询以查找所有 拥有帖子的 stackoverflow 用户:

SELECT * FROM Users
WHERE UserID IN (SELECT UserID FROM Posts)

这按预期工作;我得到了所有有帖子的用户的列表。

现在查询逆;查找所有没有有帖子的 stackoverflow 用户:

SELECT * FROM Users
WHERE UserID NOT IN (SELECT UserID FROM Posts)

这不返回任何记录,这是不正确的。

给定假设数据1

Users              Posts
================   ===============================
UserID  Username   PostID   UserID  Subject
------  --------   -------  ------  ----------------
1       atkins     1        1       Welcome to stack ov...
2       joels      2        2       Welcome all!
...     ...        ...      ...
399573  gt6989b    ...      ...
...     ...        ...      ...
                   10592    null    (deleted by nsl&fbi...
                   ...      ... 

并假设 NULL 的规则:

  • NULL = NULL 评估为未知
  • NULL <> NULL 评估为未知
  • value = NULL 评估未知

如果我们查看第二个查询,我们有兴趣在 Posts.UserID 列中找到所有 not Users.UserID 的行。我会按逻辑进行如下:

检查用户 ID 1

  • 1 = 1 返回真。所以我们断定该用户有一些帖子,并且不将它们包含在输出列表中

现在检查用户 ID 2:

  • 2 = 1 返回 false,所以我们继续寻找
  • 2 = 2 返回 true,因此我们断定该用户有一些帖子,并且不将它们包含在输出列表中

现在检查用户 ID 399573

  • 399573 = 1 返回 false,所以我们继续寻找
  • 399573 = 2 返回 false,所以我们继续寻找
  • ...
  • 399573 = null 返回未知,所以我们继续寻找
  • ...

我们没有找到用户 ID 399573 的帖子,因此我们会将他包含在输出列表中。

除了 SQL Server 不这样做。如果您的in 列表中有一个NULL,那么它会突然找到一个匹配项。 它突然找到了一个匹配项。突然399573 = null 评估为真。

为什么valuenull 的比较返回未知,除非它返回true?

编辑:我知道我可以通过专门排除空值来解决这个 nonsensical 行为:

SELECT * FROM Users
WHERE UserID NOT IN (
   SELECT UserID FROM Posts
   WHERE UserID IS NOT NULL)

但我不应该这样做,据我所知,没有它布尔逻辑应该没问题 - 因此我的问题。

脚注

  • 1 假设数据;如果你不喜欢它:补上你的羽绒服。
  • celko 现在有了自己的标签

【问题讨论】:

  • @Ian Boyd = 我不认为这是一个错误。还有一点要记住的是IN 使用orNOT IN 使用AND。当您针对具有不等式与已知值的NULL 进行评估时,您将始终得到错误,因为无法知道它是否匹配。
  • @Ian Boyd:我不会称之为错误。 IN 只不过是一系列OR 子句的便捷简写。从技术上讲,考虑另一个问题中给出的扩展的正确方法是:select 'true' where NOT(3 = 1 or 3 = 2 or 3 = null),在逻辑上等同于DeMorgan's Law。在任何情况下,谬误都是假设 value=NULL 的比较返回 FALSE,而实际上结果是 UNKNOWN。
  • 是的。不是错误。它也是 not 布尔逻辑。是3 valued logic
  • @Ian Boyd - 对于你姐姐的名字问题,这是一个数据建模问题。您应该有一个子表 SISTER 引用您的主表。如果您没有姐妹,您将不会被包含在结果集中。此外,NULL 表示未知。如果你有 2 个人的名字你不知道,你能说他们的名字之一不是克尔斯滕吗?

标签: sql-server null celko


【解决方案1】:

常见问题,固定答案:

NOT IN 子句的行为可能令人困惑,因此需要一些解释。考虑以下查询:

SELECT LastName, FirstName FROM Person.Contact WHERE LastName NOT IN('Hedlund', 'Holloway', NULL)

尽管 AdventureWorks.Person.Contact 中有超过一千个不同的姓氏,但查询没有返回任何内容。这对于初学者数据库程序员来说可能看起来违反直觉,但它实际上非常有意义。解释包括几个简单的步骤。首先,考虑以下两个查询,它们显然是等价的:

SELECT LastName, FirstName FROM Person.Contact

WHERE LastName IN('Hedlund', 'Holloway', NULL)



SELECT LastName, FirstName FROM Person.Contact

WHERE LastName='Hedlund' OR LastName='Holloway' OR LastName=NULL

请注意,这两个查询都会返回预期结果。现在,让我们回顾一下德摩根定理,它指出:

not (P and Q) = (not P) or (not Q)

not (P or Q) = (not P) and (not Q)

我正在从 Wikipedia (http://en.wikipedia.org/wiki/De_Morgan_duality) 剪切和粘贴。将 DeMorgan 定理应用于该查询,可以得出这两个查询也是等价的:

SELECT LastName, FirstName FROM Person.Contact WHERE LastName NOT IN('Hedlund', 'Holloway', NULL)



SELECT LastName, FirstName FROM Person.Contact

WHERE LastName<>'Hedlund' AND LastName<>'Holloway' AND LastName<>NULL

最后一个 LastNameNULL 永远不会为真

【讨论】:

  • 德摩根定理适用于布尔逻辑,不适用于三值逻辑。例如:not (unknown or true) = false,但是(not unknown) and (not true) = unknown,违反了定理
  • 我的困惑来自第二组“等效”查询。我会争辩说它们不相等 - 因为它们返回不同的答案。但我的论点是无关紧要的,因为 ANSI 家伙 defined 第一个是第二个的别名。
  • 接受了这个答案,因为它指出NOT IN 被定义为(x&lt;&gt;a) AND (x&lt;&gt;b) AND (x&lt;&gt;c) ... 的别名。
  • @Andomar - 实际上,我相信它是:(not unknown) and (not true) = unknown and false = false,结果与:not (unknown or true) = not (真)=假。类似地,not (unknown or false) = not (unknown) = unknown,结果相同:not (unknown and true) = not (unknown) = unknown。因此该定理适用于 3 值逻辑,前提是公理:未知或真 = 真;未知和假=假;
【解决方案2】:

你第一句话的假设不正确:

为什么要比较值 null 返回 false,除非使用 a NOT IN,在哪里返回 true?

但值与 null 的比较不会返回 false;它返回unknown。而unknown有自己的逻辑:

unknown  AND  true   = unknown
unknown  OR   true   = true
unknown  OR   false  = unknown

如何实现的一个示例:

where 1 not in (2, null)
--> where 1 <> 2 and 1 <> null
--> where true and unknown
--> where unknown

where 子句仅匹配 true,因此这会过滤掉任何行。

您可以在Wikipedia 找到 3 值逻辑的全部荣耀。

【讨论】:

  • 我将所有涉及AND null 的引用更改为unknown。问题仍然存在。
  • 我 +1 的其他答案的细节。但是对此要 +1,以提及 3 个有价值的逻辑
猜你喜欢
  • 1970-01-01
  • 2012-05-04
  • 2017-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多