这个问题中的 SQL 是针对 Oracle 的。但是您可以简单地删除FROM dual
它以使其在 SQL Server 上工作(它返回与 Oracle 相同的结果)。
我有以下 SQL:
SELECT 1
FROM dual
WHERE NULL IN (SELECT 1 FROM dual WHERE 1 = 0);
SELECT 1
FROM dual
WHERE NULL NOT IN (SELECT 1 FROM dual WHERE 1 = 0);
分别返回一个空集和 1。当我们用其他值替换 时NULL
,我们得到了完全相同的结果。
SELECT 1
FROM dual
WHERE 33 IN (SELECT 1 FROM dual WHERE 1 = 0);
SELECT 1
FROM dual
WHERE 33 NOT IN (SELECT 1 FROM dual WHERE 1 = 0);
那么NULL和空结果集的比较在这里是如何进行的呢?
让我们看看这两个条件会发生什么:
由于子查询产生一个空表,我们可以用伪代码编写它们:
所以第一个询问是否有一些(NULL)值在一个空表中。这种条件的结果总是
FALSE
,与值是否为 null 无关,因为表是空的。在第二个条件下应用相同的推理,它总是
TRUE
, 再次独立于值。在 Postgres 中很容易确认这些条件的结果值是什么,因为我们有一个布尔类型。请参阅dbfiddle.uk,其中第一个显示
f
(FALSE),第二个显示t
(TRUE)。由于上述原因,当您运行两个查询时,它们变为:
给出空结果(正确,因为
WHERE
条件是FALSE
)和
给出一行(再次正确)。
这是SQL特有的所谓“三值逻辑”的问题。与使用 和 的其他编程语言不同
TRUE
,FALSE
SQL 还具有 UNKNOWN (NULL)。SQL 的不同元素处理 UNKNOWN 的方式也不一致。ON
,WHERE
并且HAVING
都将 UNKNOWN 视为 FALSE。SQL 将空结果集视为 NULL。因此,NOT IN
它正在寻找一个错误的陈述,并且由于 WHERE 将 NULL 解释为 FALSE,您会收到您的输出。比较两个 NULL 值也会返回 UNKNOWN。另一方面,
CHECK
约束将 NULL 视为 TRUE。例如,如果您要CHECK
对高于零的销售额创建约束,则 NULL > 0 的计算结果为 UNKNOWN,但规则是约束不得计算为 FALSE,因此它最终为 TRUE。(根据 Lennart 的评论编辑)。详细解释:
(SQL 服务器语法)
返回一个空集,并且由于 SQL 将空数据集视为 NULL,因此您实际上在做的是:
更具体地说,你将它与一个所有行的表进行比较= NULL
https://www.oreilly.com/library/view/sql-and-relational/9781449319724/ch12s07.html
If a row subquery evaluates to an empty table, that empty table is coerced to a row of all nulls.
你也有它的数学部分:
http://www.solving-math-problems.com/null-set-empty-set.html
比较两个 NULL 时,结果始终为 UNKNOWN,而 WHERE 语句将 UNKNOWN 视为 FALSE。
因此,在您的第一个代码中:
您的 WHERE 语句基本上是检查 NULL 是否存在于 NULL 集合中,并且由于 NULL 的比较是 UNKNOWN 并且 UNKNOWN 被视为 FALSE,因此您不会得到任何结果。
但在这儿:
您是说 NULL 集中不存在 WHERE NULL。同样,由于 UNKNOWN 被视为 FALSE,您会得到一个输出。
这也解释了为什么当你比较:
您收到了输出。因为 33 不在 NULL Set/Empty Set 中的元素中。