当我得到不同的记录计数时,这个问题出现了,我认为是相同的查询,一个使用not in where约束,另一个使用左连接。not in约束中的表有一个空值(坏数据),导致该查询返回0条记录计数。我有点理解为什么,但我需要一些帮助来充分理解这个概念。
简单地说,为什么查询A返回结果而B没有?
A: select 'true' where 3 in (1, 2, 3, null)
B: select 'true' where 3 not in (1, 2, null)
这是在SQL Server 2005上。我还发现调用set ansi_nulls off会导致B返回一个结果。
Compare to null没有定义,除非你使用is null。
因此,当比较3和NULL(查询A)时,它返回undefined。
例如,SELECT 'true' where 3 in (1,2,null)
而且
SELECT 'true' where 3 not in (1,2,null)
将产生相同的结果,因为NOT (UNDEFINED)仍然是UNDEFINED,但不是TRUE
SQL使用三值逻辑来表示真值。IN查询产生预期的结果:
SELECT * FROM (VALUES (1), (2)) AS tbl(col) WHERE col IN (NULL, 1)
-- returns first row
但是添加一个NOT并不会颠倒结果:
SELECT * FROM (VALUES (1), (2)) AS tbl(col) WHERE NOT col IN (NULL, 1)
-- returns zero rows
这是因为上面的查询与以下查询是等价的:
SELECT * FROM (VALUES (1), (2)) AS tbl(col) WHERE NOT (col = NULL OR col = 1)
下面是where子句的计算方法:
| col | col = NULL⁽¹⁾ | col = 1 | col = NULL OR col = 1 | NOT (col = NULL OR col = 1) |
|-----|----------------|---------|-----------------------|-----------------------------|
| 1 | UNKNOWN | TRUE | TRUE | FALSE |
| 2 | UNKNOWN | FALSE | UNKNOWN⁽²⁾ | UNKNOWN⁽³⁾ |
注意:
涉及NULL的比较结果为UNKNOWN
没有一个操作数为TRUE且至少有一个操作数为UNKNOWN的OR表达式的结果为UNKNOWN (ref)
UNKNOWN的NOT产生UNKNOWN(参考)
您可以将上面的示例扩展到两个以上的值(例如NULL, 1和2),但结果将是相同的:如果其中一个值为NULL,则没有行将匹配。
Null表示数据的缺失,即它是未知的,而不是一个无数据值。具有编程背景的人很容易混淆这一点,因为在C类型语言中,当使用指针时,null实际上是空的。
因此,在第一种情况下,3确实在(1,2,3,null)的集合中,因此返回true
在第二种情况下,你可以把它简化为
选择“true”where 3 not in (null)
所以没有返回任何东西,因为解析器不知道与之进行比较的集合——它不是一个空集,而是一个未知集。使用(1,2,null)没有帮助因为(1,2)集合显然是假的,但是你用它来对抗未知,它是未知的。