在NOT IN子句中的NULL值
在NOT IN子句中的NULL值
当我以为是相同的查询时,我得到了不同的记录计数,其中一个使用了not in
where
约束,另一个使用了left join
。在not in
约束中的表中有一个空值(坏数据),这导致该查询返回0条记录。我有点理解为什么会这样,但我需要一些帮助完全理解这个概念。\n简单来说,为什么查询A返回结果而查询B不返回结果?\n
A: select 'true' where 3 in (1, 2, 3, null) B: select 'true' where 3 not in (1, 2, null)
\n这是在SQL Server 2005上的情况。我还发现调用set ansi_nulls off
会导致查询B返回结果。
在使用NULL时,你实际上是在处理三值逻辑。你的第一个查询返回结果,因为WHERE子句的计算结果是:
3 = 1 or 3 = 2 or 3 = 3 or 3 = null 这是: FALSE or FALSE or TRUE or UNKNOWN 计算结果是 TRUE
第二个查询:
3 <> 1 and 3 <> 2 and 3 <> null 计算结果是: TRUE and TRUE and UNKNOWN 计算结果是: UNKNOWN
UNKNOWN不同于FALSE,你可以通过调用以下代码进行测试:
select 'true' where 3 <> null select 'true' where not (3 <> null)
这两个查询将不会给出结果。
如果UNKNOWN等同于FALSE,那么假设第一个查询将会得到FALSE,那么第二个查询将会评估为TRUE,因为它将与NOT(FALSE)相同。
但事实并非如此。
关于这个主题有一篇非常好的文章,你可以在SqlServerCentral上找到。
整个NULL和三值逻辑的问题可能有点令人困惑,但是理解它是写出正确的TSQL查询的关键。
我还推荐另一篇文章,关于SQL聚合函数和NULL的问题。
在使用NOT IN
子句时,如果在可能值的列表中包含NULL
或者NULL
,将会返回0条记录。这是因为NULL
是一个未知的值,无法确定NULL
值是否是要测试的值。
出现这个问题的原因是NOT IN
子句无法处理NULL
值。在比较时,NULL
会导致查询无法确定是否匹配,因此会返回0条记录。
解决这个问题的方法是使用NOT EXISTS
子句来替代NOT IN
子句。在NOT EXISTS
子句中,可以使用IS NULL
来判断是否存在NULL
值。
以下是一个示例,演示如何使用NOT EXISTS
来解决这个问题:
SELECT column_name FROM table_name WHERE NOT EXISTS ( SELECT 1 FROM another_table WHERE another_table.column_name = table_name.column_name AND another_table.column_name IS NULL );
通过使用NOT EXISTS
子句和IS NULL
条件,可以避免NULL
值导致NOT IN
子句返回0条记录的问题。
在上述内容中,描述了一个关于在NOT IN子句中使用NULL值的问题。问题的原因是,在使用NOT IN子句时,NULL值的处理方式不一致,导致查询结果与预期不符。解决方法是将NOT IN子句转换为一系列的<>(不等于)操作符,并使用IS NULL来处理NULL值。
具体来说,Query A中的代码是:
select 'true' where 3 = 1 or 3 = 2 or 3 = 3 or 3 = null
由于3 = 3为真,因此会返回结果。
Query B中的代码是:
select 'true' where 3 <> 1 and 3 <> 2 and 3 <> null
当ansi_nulls开启时,3 <> null的结果为UNKNOWN,因此谓词的结果为UNKNOWN,不会返回任何行。当ansi_nulls关闭时,3 <> null的结果为真,因此谓词的结果为真,会返回一行。
问题的关键在于将NOT IN转换为一系列的<>操作符时,改变了"not in this set"的语义行为。
此外,还提到了一些其他相关的问题和观点,例如:
- SQL Server的行为是错误的,因为它应该使用"IS NULL"来处理NULL值的比较,而不是错误地应用错误的语义。
- 在某些SQL解释器中,查询可以正常执行,但在其他解释器中不起作用。
- 关于NULL值的解释可能存在混淆,因为NULL可以被解释为"某个未知的值",所以3在(1, 2, 3, unknown)中是真的,而3不在(1, 2, unknown)中既不是真也不是假。
- NULL值的布尔转换可能会导致一些意外的结果。
为了解决在NOT IN子句中使用NULL值的问题,可以将NOT IN转换为一系列的<>操作符,并使用IS NULL来处理NULL值。此外,需要注意NULL值的处理方式可能会导致一些意外的结果。