为什么value
与null
的比较返回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列中找到Users.UserID 不的所有行。我会按照以下逻辑进行:
检查UserID 1
1 = 1
返回true。因此,我们得出结论,该用户有一些帖子,并且不将它们包含在输出列表中现在检查UserID 2:
2 = 1
返回false,所以我们一直在寻找2 = 2
返回true,因此我们得出结论,此用户有一些帖子,并且不将它们包含在输出列表中现在检查UserID 399573
399573 = 1
返回false,所以我们一直在寻找399573 = 2
返回false,所以我们一直在寻找399573 = null
返回未知,所以我们一直在寻找我们没有找到UserID 399573的帖子,因此我们会将他包含在输出列表中。
除了SQL Server不这样做。如果您的in
列表中有NULL,则会突然找到匹配项。 突然发现匹配。突然399573 = null
评估为真。
为什么value
与null
的比较会返回未知,除非它返回true?
编辑:我知道我可以通过明确排除空值来解决此无意义行为:
SELECT * FROM Users
WHERE UserID NOT IN (
SELECT UserID FROM Posts
WHERE UserID IS NOT NULL)
但是我不应该这样做,据我所知,布尔逻辑在没有它的情况下应该没问题 - 因此我的问题。
答案 0 :(得分:9)
你的第一句话中的假设是不正确的:
为什么值的比较 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值逻辑的全部荣耀。
答案 1 :(得分:8)
常见问题,罐头答案:
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
请注意,两个查询都会返回预期结果。现在,让我们回顾一下DeMorgan的定理,该定理指出:
not (P and Q) = (not P) or (not Q)
not (P or Q) = (not P) and (not Q)
我正在从维基百科(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
这最后一个LastName&lt;&gt; NULL永远不会是真的