为什么NULL = NULL在SQL Server中评估为false

时间:2009-12-03 22:28:14

标签: sql sql-server null

在SQL Server中,如果where子句中有nullParam=NULL,则它总是计算为false。这是违反直觉的,并且给我带来了许多错误。我理解IS NULLIS NOT NULL关键字是正确的方法。但是为什么SQL服务器会以这种方式运行?

20 个答案:

答案 0 :(得分:188)

在这种情况下将null视为“未知”(或“不存在”)。在任何一种情况下,你都不能说它们是平等的,因为你不知道它们中的任何一个的价值。因此,null = null的计算结果为true(false或null,具体取决于您的系统),因为您不知道值是否相等。此行为在ANSI SQL-92标准中定义。

编辑: 这取决于您的ansi_nulls设置。如果你关闭ANSI_NULLS,这将评估为true。运行以下代码以获取示例...

set ansi_nulls off

if null = null
    print 'true'
else
    print 'false'


set ansi_nulls ON

if null = null
    print 'true'
else
    print 'false'

答案 1 :(得分:113)

弗兰克多大了?我不知道(null)。

雪莉多大了?我不知道(null)。

Frank和Shirley的年龄相同吗?

正确答案应该是“我不知道”(null),而不是“不”,因为Frank和Shirley 可能年龄相同,我们根本就不知道。

答案 2 :(得分:24)

答案 3 :(得分:8)

也许这取决于,但我认为NULL=NULL评估为NULL,就像大多数以NULL为操作数的操作一样。

答案 4 :(得分:7)

仅仅因为你不知道两件事是什么,并不代表他们是平等的。如果你想到NULL你想到“NULL”(字符串)那么你可能想要一个不同的平等测试,如Postgresql的IS DISTINCT FROMIS NOT DISTINCT FROM

来自PostgreSQL docs on "Comparison Functions and Operators"

  

表达式IS DISTINCT FROM表达式

     

表达式IS NOT DISTINCT FROM表达式

     

对于非空输入,IS DISTINCT FROM<>运算符相同。但是,如果两个输入都为null,则返回false,如果只有一个输入为null,则返回true。类似地,对于非空输入,IS NOT DISTINCT FROM=相同,但是当两个输入都为空时它返回true,而当只有一个输入为空时返回false。因此,这些结构有效地表现为null是正常数据值,而不是“未知”。

答案 5 :(得分:4)

NULL不等于任何东西,甚至不是自己。我理解NULL行为的个人解决方案是尽可能避免使用它:)。

答案 6 :(得分:4)

technet处,可以很好地解释null值的工作原理。

Null意味着未知。

因此布尔表达式

value = null

不计算为false,它的计算结果为null,但如果这是where子句的最终结果,则不返回任何内容。这是一种实用的方法,因为返回null将很难设想。

了解以下内容非常有趣且非常重要

如果在查询中我们有

where (value=@param Or @param is null) And id=@anotherParam

  • value = 1
  • @param为null
  • id = 123
  • @ anotherParam = 123

然后

“value = @ param”评估为null “@param为null”评估为真 “id = @ anotherParam”评估为true

因此要评估的表达式变为

(null或true)真实

我们可能会想到这里“null或true”将被计算为null,因此整个表达式变为null并且不会返回该行。

事实并非如此。为什么?

因为“null或true”求值为true,这是非常合乎逻辑的,因为如果一个操作数对于Or运算符为真,那么无论另一个操作数的值如何,操作都将返回true。因此,其他操作数未知(null)并不重要。

所以我们最终得到true = true,因此将返回该行。

注意:使用相同的清晰逻辑“null或true”求值为true,“null和true”求值为null。

<强>更新
好的,只是为了使它完整我想在这里添加其余部分,结果与上述相关非常有趣。

“null或false”计算结果为null,“null和false”计算结果为false。 :)

逻辑当然仍然像以前一样不言而喻。

答案 7 :(得分:4)

至少可以说,NULL的概念值得怀疑。 Codd在上下文中介绍了关系模型和NULL的概念(并继续提出多种类型的NULL!)然而,自从Codd的原始着作以来,关系理论已经发展:他的一些提议已被删除(例如主键)和其他人从未接触过(例如theta运营商)。在现代关系理论(真正的关系理论,我应该强调)中,NULL根本就不存在。参见第三份宣言。 http://www.thethirdmanifesto.com/

SQL语言存在向后兼容性问题。 NULL找到了进入SQL的方式,我们坚持使用它。可以说,SQL中NULL的实现存在缺陷(SQL Server的实现由于其ANSI_NULLS选项而使事情变得更加复杂)。

我建议避免在基表中使用NULLable列。


虽然也许我不应该受到诱惑,但我只想断言我自己对SQL NULL如何工作的修正:

NULL = NULL评估为UNKNOWN

UNKNOWN是一个逻辑值。

NULL是一个数据值。

这很容易证明,例如

SELECT NULL = NULL

在SQL Server中正确生成错误。如果结果是数据值,那么我们希望看到NULL,因为这里的一些答案(错误地)表明我们会这样做。

逻辑值UNKNOWN在SQL DML和SQL DDL中的处理方式不同。

在SQL DML中,UNKNOWN会导致从结果集中删除行。

例如:

CREATE TABLE MyTable
(
 key_col INTEGER NOT NULL UNIQUE, 
 data_col INTEGER
 CHECK (data_col = 55)
);

INSERT INTO MyTable (key_col, data_col)
   VALUES (1, NULL);

即使INSERT条件解析为CHECK,此行的NULL = NULL也会成功。这是在SQL-92(“ANSI”)标准中定义的:

  

11.6表约束定义

     

3)

     

如果表约束是一个检查   约束定义,然后让SC成为   搜索条件立即   包含在检查约束中   定义,让T为表名   包含在相应的表中   约束描述符;桌子   如果和,则不满足约束   只有

     

EXISTS(SELECT * FROM T)   (SC))

     

是真的。

按照逻辑再次仔细阅读。

用简单的英语,我们上面的新行给出了UNKNOWN并允许通过的“怀疑的好处”。

在SQL DML中,WHERE子句的规则更容易理解:

  

搜索条件适用于   T的每一行。结果在哪里   子句是T的那些行的表   搜索结果   条件是真的。

简单地说,评估为UNKNOWN的行将从结果集中删除。

答案 8 :(得分:3)

因为NULL表示“未知值”且两个未知值不相等。

所以,如果我们的逻辑NULL N°1等于NULL N°2,那么我们必须以某种方式告诉你:

SELECT 1
WHERE ISNULL(nullParam1, -1) = ISNULL(nullParam2, -1)

其中已知值-1 N°1等于-1 N°2

答案 9 :(得分:3)

MSDN在空值上有一个很好的描述性article和它们产生的三个状态逻辑。

简而言之,SQL92规范将NULL定义为未知,并且以下运算符中使用的NUL会导致未初始化的意外结果:

= operator NULL   true   false 
NULL       NULL   NULL   NULL
true       NULL   true   false
false      NULL   false  true

and op     NULL   true   false 
NULL       NULL   NULL   false
true       NULL   true   false
false      false  false  false

or op      NULL   true   false 
NULL       NULL   true   NULL
true       true   true   true
false      NULL   true   false

答案 10 :(得分:3)

混淆源于使用NULL 产生的间接(抽象)级别。

回到圣诞树下的&#34;&#34;&#34;比喻,&#34;未知&#34;描述了关于框A中的内容的知识状态。

因此,如果您不知道方框A中的内容,您说它是&#34;未知&#34;,但并不意味着&#34; #34;未知&#34;在框内。盒子里面不是未知的东西,可能是某种物体,或者箱子里什么都没有。

同样,如果你不知道方框B中的内容,你可以将你对内容的了解状态标记为&#34;未知&#34;。

所以这里是踢球者:你对盒子A的知识状态等于你对盒子B的了解状态。 (在这两种情况下你的知识状态是&#34;未知&#34;或者&#34;我不知道框中的内容&#34;。)但是框的内容可以是可能不平等。

回到SQL,理想情况下,只有在知道它们是什么时才能比较值。 不幸的是,描述缺乏知识的标签存储在单元格本身中,因此我们很想将其用作值。但我们不应该将其作为一种价值,因为当我们不知道方框A和/或我们是什么时,它会导致方框A的内容等于方框B的内容。不知道方框B中的内容。 (从逻辑上讲,这意味着&#34;如果我不知道方框A中的内容,如果我不知道方框B中的内容,那么那些内容是什么?方框A =方框B&#34;中的内容是假的。)

耶,死马。

答案 11 :(得分:2)

问题:
一个未知的是否等于另一个未知的? (NULL = NULL)
这个问题是没有人可以回答的问题,因此默认为true或false,具体取决于你的ansi_nulls设置。

然而问题是:
这个未知变量是未知的吗? 这个问题非常不同,可以回答真实。

nullVariable = null正在比较值
nullVariable为null是比较变量的状态

答案 12 :(得分:1)

在 WHERE 子句中处理 NULL = NULL 比较有两种合理的方法,它们归结为“NULL 是什么意思?”一种方法假设 NULL 表示“未知”,另一种假设 NULL 表示“数据不存在”。 SQL 选择了第三种方式,这是错误的。

“NULL 表示未知”解决方案: 抛出错误。

Unknown = unknown 应评估为 3VL null。但是 WHERE 子句的输出是 2VL:要么返回该行,要么不返回。这就像被要求除以零并返回一个数字:没有正确的响应。因此,您改为抛出错误,并强制程序员明确处理这种情况。

“NULL 表示没有数据”解决方案: 返回行。

无数据 = 无数据应评估为真。如果我比较两个人,他们的名字和姓氏都一样,而且都没有中间名,那么说“这些人有相同的名字”是正确的。

SQL 解决方案: 不要返回行。

总是错误的。如果 NULL 表示“未知”,那么您不知道是否应该返回该行,并且您不应该尝试猜测。如果 NULL 表示“没有数据”,那么您应该返回该行。无论哪种方式,静默删除该行都是不正确的,并且会导致问题。这是两个世界中最糟糕的。

暂且不谈理论,我和 AlexDev 在一起:我几乎从未遇到过“返回行”不是预期结果的情况。但是,“几乎从不”并非“从不”,SQL 数据库经常作为大型重要系统的骨干,因此我可以看到严谨和抛出错误的合理案例。

我看不到的是将 3VL null 强制转换为 2VL false 的情况。像大多数无声类型的强制一样,它是一只等待在你的系统中被释放的狂暴黄鼠狼,当黄鼠狼最终跳出来咬人时,你就会有一个时间的快乐恶魔追踪它回到它的巢穴。

答案 13 :(得分:0)

null在sql中是未知的,所以我们不能指望两个未知数是相同的。

但是,您可以通过将ANSI_NULLS设置为Off(默认为On)来获取该行为 您将能够使用= operator for nulls

SET ANSI_NULLS off
if null=null
print 1
else 
print 2
set ansi_nulls on
if null=null
print 1
else 
print 2

答案 14 :(得分:0)

只是对其他精彩答案的补充:

AND: The result of true and unknown is unknown, false and unknown is false,
while unknown and unknown is unknown.

OR: The result of true or unknown is true, false or unknown is unknown, while unknown or unknown is unknown.

NOT: The result of not unknown is unknown

答案 15 :(得分:0)

这里的答案似乎都是从CS的角度出发的,所以我想从开发人员的角度来补充。

对于开发人员,NULL非常有用。这里的答案说NULL表示未知,也许在CS理论中是正确的,不记得了,已经有一段时间了。但是,在实际开发中,至少以我的经验来看,这种情况发生的时间约为1%。其余的99%用于值不是未知但未知的情况。

例如:

  • Client.LastPurchase,用于新客户。这不是未知数,众所周知他还没有购买。

  • 在每个Table Class映射中使用Hierarchy的ORM时,某些类的某些值只是不映射。

  • 在映射tree structure时,根通常具有Parent = NULL

  • 还有更多...

我敢肯定大多数开发人员在某个时候都写过WHERE value = NULL, 没有得到任何结果,这就是他们了解IS NULL语法的方式。只要看一下这个问题和相关问题的票数即可。

SQL数据库是一种工具,应该以最易于用户理解的方式设计它们。

答案 16 :(得分:0)

如果要查找对两个NULL返回true的表达式,则可以使用:

SELECT 1 
WHERE EXISTS (
    SELECT NULL
    INTERSECT
    SELECT NULL
)

如果要将数据从一个表复制到另一个表,这将很有帮助。

答案 17 :(得分:0)

相等性测试(例如,在case语句中的when子句中)可以从

更改
XYZ = NULL 

XYZ IS NULL

如果我想将空格和空字符串视为NULL,我通常还会使用相等测试:

(NULLIF(ltrim( XYZ ),'') IS NULL)

答案 18 :(得分:0)

再次引用圣诞节的比喻:

在SQL中,NULL基本上表示“封闭框” (未知)。因此,比较两个封闭框的结果也将是未知的(空)。

对于开发人员,我理解这是违反直觉的,因为在编程语言中,通常NULL表示“空框” (已知)。比较两个空盒子自然会得出true / equal。

这就是为什么JavaScript例如区分nullundefined的原因。

答案 19 :(得分:-1)

您为政府登记有关公民的信息工作。这包括该国每个人的身份证。大约40年前,一个孩子被留在教堂门口,没人知道他们的父母是谁。此人的父亲ID是NULL。存在两个这样的人。计算与至少一个其他人(兄弟姐妹的人)共享同一父亲ID的人。你也算这两个吗?

答案是否定的,你不是,因为我们不知道他们是否是兄弟姐妹。

假设您没有NULL选项,而是使用一些预定值来表示“未知数”,例如空字符串或数字0或*字符,等等。那么您将在您的查询中具有* = *,0 = 0和“” =“”等。这不是您想要的(按照上面的示例),并且您可能经常忘记这些情况(上面的示例是一个清晰的边缘案例,而不是日常的日常思考),那么您需要用这种语言来记住NULL = NULL是错误的。

必要性是发明之母。