给定查询以查找具有帖子的所有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不这样做.如果你的列表中有NULL,那么它会突然找到匹配项.它突然找到一个匹配.突然399573 = null计算结果为true.
为什么value与null的比较返回未知,除非它返回true?
编辑:我知道我可以通过专门排除空值来解决这种荒谬的行为:
SELECT * FROM Users WHERE UserID NOT IN ( SELECT UserID FROM Posts WHERE UserID IS NOT NULL)
但我不应该,只要我可以告诉布尔逻辑没有它应该没问题 – 因此我的问题.
脚注
> 1个假设数据;如果你不喜欢它:弥补你的失望.
> celko现在有自己的标签
解决方法
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',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<>'Hedlund' AND LastName<>'Holloway' AND LastName<>NULL
最后一个LastName<> NULL永远不会成立