为什么NULL = NULL在SQL Server中计算为false

在SQL服务器中,如果在where子句中有nullParam=NULL ,它总是计算为false。 这是违反直觉的,造成了很多错误。 我明白IS NULLIS NOT NULL关键字是正确的方法。 但为什么SQL服务器的行为呢?

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

编辑:这取决于您的ansi_nulls设置。 如果您closures了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' 

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

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

弗兰克和雪莉是同龄吗?

正确的答案应该是“我不知道”(null),而不是“否”,因为弗兰克和雪莉可能是同龄人,我们根本就不知道。

在这里我希望能澄清我的立场。

NULL = NULL评估为错误是错误的。 黑客和先生正确地回答了NULL 。 这是为什么。 Dewayne Christensen写信给我,在对Scott Ivey的评论中:

从十二月份开始,我们用一个季节性的例子。 我在树下有两件礼物。 现在,你告诉我是否有两件相同的事情。

他们可以是不同的,也可以是平等的,直到你打开这两个礼物, 你才会知道 。 谁知道? 你邀请了两个彼此不认识的人,并且都向你做了同样的礼物 – 很less,但不是不可能的。

所以问题是:这两个UNKNOWN呈现相同(等于,=)? 正确的答案是:UNKNOWN(即NULL )。

这个例子是为了certificate “..( falsenull ,取决于你的系统)..”是一个正确的答案 – 它不是, 只有 NULL是正确的3VL(或者你可以接受一个系统给出错误的答案?)

这个问题的正确答案必须强调这两点:

  • 三值逻辑(3VL)是违反直觉的(参见Stackoverflow和其他论坛上的无数其他问题)。
  • 基于SQL的DBMS通常甚至不尊重3VL,他们有时会给出错误的答案(如原来的招贴声明,SQL Server在这种情况下)。

所以我重申一下:SQL强迫人们解释平等的自反性质并不是一件好事,它说:

for any x, x = x x§§ (用简单的英语:任何宇宙的话语, “物”总是等于它本身 )。

..在3VL( TRUEFALSENULL )。 人们的期望将符合2VL( TRUEFALSE ,即使在SQL中,对于所有其他值也是如此),即x = x 总是评估为 TRUE ,对于任何可能的x值 – 没有例外。

还要注意,NULL是有效的“ 非值 ”(正如他们的辩护人假装他们是),哪一个可以指定为属性值(??)作为关系variables的一部分。 所以它们是每种types(域)的可接受值,而不仅仅是逻辑expression式的types。

这就是我的观点 :作为价值, NULL值是一个“怪兽”。 没有委婉的说法,我宁愿说: 废话

我认为这个表述更加清晰,而且没有太多的争议 – 对于我英文水平差的问题,感到抱歉。

这只是NULL的问题之一。 在可能的情况下尽量避免使用它们。

§这里我们关心的是价值观 ,所以这两个礼物总是两个不同的物理对象并不是一个有效的反对意见。 如果你不相信,我很抱歉,这不是解释价值和“对象”语义之间区别的地方(关系代数从一开始就具有价值语义 – 参见Codd的信息原理;我认为一些SQL DBMS实现者甚至不关心一个普通的语义)。

§据我所知,这是一个公理(从某种forms或另一种forms,但总是用2VL解释)接受的公理,而这正是因为它非常直观。 3VL(实际上是一个逻辑系列)是一个更新的发展(但我不知道什么时候第一次开发)。

注意:如果有人会介绍底层 , 单元和选项types作为尝试certificateSQL NULL,我只有在经过非常详细的检查之后才能确信,它将显示SQL实现如何具有NULLtypes的系统,并将澄清,最后,什么是空值(这些“值 – 不完全值”)真的是。


接下来我会引用一些作者。 任何错误或遗漏可能是我的,而不是原作者。

Joe Celko在SQL NULL上

我看到Joe Celko经常在这个论坛上引用。 显然他在这里是一位备受推崇的作家。 于是,我对自己说:“他写了关于SQL NULL的什么东西?他如何解释NULL的许多问题呢?” 我的一个朋友有一个电子书版本的Joe Celko的聪明人的SQL:高级SQL编程,第3版 。 让我们来看看。

首先是目录。 最让我印象深刻的是NULL被提及的次数以及在各种不同的情况下:

3.4算术和NULL 109
3.5将值转换为空值110
3.5.1 NULLIF()函数110
6 NULLs:缺lessSQL中的数据185
6.4比较NULL 190
6.5空值和逻辑190
6.5.1子查询中的NULLS谓词191
6.5.2标准SQL解决scheme193
6.6math和空值193
6.7函数和NULL 193
6.8 NULL和主机语言194
6.9 NULL的devisebuild议195
6.9.1避免主机程序中的NULL
6.10关于多个NULL值的注意事项198
10.1 IS NULL谓词241
10.1.1 NULL的来源242

等等。 它给我敲响了“讨厌的特例”。

为了版权的原因,我将从这本书中摘录一些这样的案例,试图将自己限制在本质上。 我认为这些引用属于“合理使用”原则,甚至可以刺激购买这本书 – 所以我希望没有人会抱怨(否则我将需要删除大部分,如果不是全部)。 此外,出于同样的原因,我不会报告代码片段。 对于那个很抱歉。 购买这本书阅读关于数据挖掘的推理。

在后面的括号之间的页码。

NOT NULL约束(11)

最重要的列约束是NOT NULL,它禁止在列中使用NULL。 例行使用这个约束,只有当你有充分的理由时才去除它。 当您对数据进行查询时,它将帮助您避免NULL值的复杂性。

这不是一个价值 ; 这是一个标志,可以持有价值的地方。

再一次这个“价值而不是一个价值”是无稽之谈。 其余的对我来说似乎相当明智。

(12)

简而言之,NULL会在SQL中引起很多不规则的特性,我们将在后面讨论。 你最好的select就是记住NULL的情况和规则,当你无法避免的时候。

SQL,NULL和无限的apropos:

(104)第三章:SQL中的数字数据

由于以下几个原因,SQL没有接受math的IEEE模型。

如果在SQL中允许IEEE的math规则,那么我们需要无限的types转换规则,并在转换后表示无限精确的数值。 人们对NULL有足够的麻烦,所以我们不要去那里。

SQL实现未确定在特定情况下NULL的真正含义:

3.6.2指数函数(116)

问题是当(x <= 0)时对数是未定义的。 有些SQL实现返回错误消息,有些返回NULL和DB2 / 400; 版本3发行版1返回* NEGINF(“负无穷大”的缩写)作为其结果。

Joe Celko引用David McGoveran和CJdate:

6 NULL:SQL中缺less数据(185)

David McGoveran和CJ Date在他们的书“Sybase和SQL Server指南 ”中指出:“至less现在在SQL中定义和实现,这是作者的看法,而不是NULL,比它们的价值要麻烦得多,应该避免。 他们performance出非常奇怪和不一致的行为,可能是一个错误和混乱的丰富来源。 (请注意,这些评论和批评适用于任何支持SQL样式的NULL的系统,而不仅仅是针对SQL Server。)“

作为吸毒成瘾的 NULL:

(187分之186)

在本书的其余部分, 我会敦促你不要使用它们 ,这看起来可能矛盾,但事实并非如此。 把NULL看成一种毒品; 正确使用它,它适用于你,但滥用它,它可以毁了一切。 你最好的策略是尽可能地避免NULL,并在必要时正确使用它们。

我这里唯一的反对意见是“恰当地使用它们”,它与具体的实施行为有很大的关系。

6.5.1子查询谓词中的NULLS(191/192)

人们忘记了一个子查询经常隐藏一个与NULL的比较。 考虑这两个表格:

结果将是空的。 这是违反直觉的 ,但是是正确的。

(分隔器)

6.5.2标准的SQL解决scheme(193)

SQL-92通过添加一个新的谓词来解决了一些3VL(三值逻辑)问题:

<search条件> IS [NOT] TRUE | FALSE | 未知

但UNKNOWN本身就是一个问题的来源,所以CJ Date在下面引用的书中,在第4.5章中有所build议 在SQL中避免空值

  • 不要在任何情况下使用关键字UNKNOWN。

阅读下面链接的UNKNOWN上的“ASIDE” 。

6.8 NULL和主机语言(194)

但是,您必须知道如何将NULL传递到主机程序时才处理。 没有定义embedded的标准主机语言支持NULL,这是避免在数据库模式中使用它们的另一个很好的理由。

(分隔器)

6.9 NULL的devisebuild议(195)

尽可能在所有列上声明所有具有NOT NULL约束的基表是个好主意。 NULL使不知道SQL的人感到困惑,而NULL是昂贵的。

异议:即使是熟悉SQL的人也会迷惑NULL,请参阅下文。

(195)

在FOREIGN KEY中应该避免NULL。 SQL允许这种“怀疑的好处”关系,但它可能会导致涉及连接的查询中的信息丢失。 例如,给定一个由订单表引用为FOREIGN KEY的库存中的零件号代码,在获取具有NULL的零件清单时会遇到问题。 这是强制性的关系; 您不能订购不存在的零件。

(分隔器)

6.9.1避免来自主机程序的NULL(197)

您可以避免将一些编程规则的主机程序中的NULL值放入数据库中。

  1. 确定缺失数据对编程和报告的影响: 使用NULL的数字列是一个问题,因为使用集合函数的查询可能会提供令人误解的结果。

(分隔器)

(227)

一个空集的SUM()总是NULL。 使用这个技巧时最常见的编程错误之一是编写一个可以返回多行的查询。 如果你没有想到,你可能写了最后一个例子:…

(分隔器)

10.1.1 NULL的来源(242)

记住NULL可能发生的地方很重要。 它们不仅仅是一列中可能的价值 。 空集,OUTER JOIN,具有NULL的算术expression式和OLAP运算符的集合函数都返回NULL。 这些结构通常在VIEW中显示为列。

(分隔器)

(301)

当您尝试将IN谓词转换为EXISTS谓词时,会发现NULL的另一个问题。

(分隔器)

16.3所有谓词和极值函数(313)

首先,这两个谓词在SQL中是不一样的:

但是您必须记住极值函数的规则 – 在返回更大或最小值之前,它们将删除所有的NULL值。 ALL谓词不会删除NULL,所以您可以将它们放入结果中。

(分隔器)

(315)

但是,标准中的定义是否定的,所以NULL可以得到疑问的好处。 …

正如你所看到的,在UNIQUE约束中避免NULL是一个好主意。

讨论GROUP BY:

将NULL视为彼此相等 ,并形成自己的组。 然后每个组在一个新的结果表中简化为一行,replace旧的结果。

这意味着对于GROUP BY子句,NULL = NULL并不像NULL那样计算为NULL,而是计算为TRUE。

SQL标准令人困惑:

ORDER BY和NULLs(329)

是否将NULL值的sorting键值视为大于或小于非NULL值是实现定义的,但是…

有SQL产品可以这样做。

1999年3月,Chris Farrar提出了一个他的开发者提出的一个问题,他让他检查了我认为我理解的SQL标准的一部分 。 克里斯发现了一般理解和规范的实际措辞之间的一些区别

等等。 我觉得Celko已经足够了。

CJdate在SQL NULLs

CJ Date对于NULL更加激进:在SQL,句点中避免NULL。 实际上,他的SQL和关系理论的第四章“如何编写准确的SQL代码”的标题是“没有重复,没有规定”,其子代码是“4.4什么是空错? 和“4.5避免SQL中的空值”(请点击链接:感谢谷歌图书,你可以在线阅读一些页面)。

Fabian Pascal在SQL NULL上

数据库pipe理中的实际问题 – 思考从业人员的参考 (不在网上摘抄,抱歉):

10.3实践意义

10.3.1 SQL NULL

… SQL遭受3VL固有的问题,以及许多怪癖,并发症,违背直觉和彻底的错误[10,11]。 其中包括以下内容:

  • 集合函数(例如SUM(),AVG())忽略NULL(COUNT()除外)。
  • 没有行的表上的标量expression式不正确地评估为NULL,而不是0。
  • expression式“NULL = NULL”的计算结果为NULL,但在SQL中实际上是无效的; 但是ORDER BY将NULL视为相等(无论它们在DBMS供应商之前还是在“常规”值之后)。
  • expression式“x IS NOT NULL”不等于“NOT(x IS NULL)”,就像在2VL中那样。

所有商业实现的SQL方言都遵循这种3VL方法,因此,他们不仅仅怀疑这些问题,而且还存在执行问题,这些问题因产品而异

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

只因为你不知道两件事情,并不意味着它们是平等的。 如果当你想到NULL你会想到“NULL”(string),那么你可能想要一个不同的testing,如Postgresql的IS DISTINCT FROM IS NOT DISTINCT FROM

http://www.postgresql.org/docs/8.4/static/functions-comparison.html

expression式是DISTINCT FROMexpression式

expression式不是DISTINCT FROMexpression式

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

NULL不等于任何东西,甚至不等于它本身。 理解NULL行为的个人解决scheme是尽可能地避免使用它。

NULL的概念至less可以说是值得怀疑的。 Codd在上下文中引入了关系模型和NULL的概念(并且提出了多于一种的NULL)然而,自从Codd的原始着作以来,关系理论已经发展了:他的一些build议已经被抛弃了(例如主键)和其他人从未被抓住(例如theta操作员)。 在现代的关系理论(真正的关系理论,我要强调)NULL根本不存在。 见第三个宣言。 http://www.thethirdmanifesto.com/

SQL语言遭受向后兼容的问题。 NULL发现它进入SQL的方式,我们坚持下去。 可以说,在SQL中实现NULL是有缺陷的(SQL Server的实现使得事情更加复杂,因为它的ANSI_NULLS选项)。

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


虽然也许我不应该被诱惑,但我只是想断言一下我自己关于NULL在SQL中的工作方式:

NULL = NULL计算为UNKNOWN

UNKNOWN是一个逻辑值。

NULL是一个数据值。

这很容易certificate,例如

SELECT NULL = NULL

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

在SQL DML和SQL DDL中分别处理逻辑值UNKNOWN

在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); 

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

11.6表约束定义

3)

如果表约束是一个检查约束定义,则令SC为检查约束定义中立即包含的search条件,并且令T为包含在相应表约束描述符中的表名; 表约束不满足当且仅当

EXISTS(SELECT * FROM T WHERE NOT(SC))

是真的。

遵循逻辑,仔细阅读。

用简单的英语,我们上面的新行被赋予“ UNKNOWN ”的好处,并被允许通过。

在SQL DML中, WHERE子句的规则更容易遵循:

search条件应用于T的每一行。where子句的结果是T的那些行,其search条件的结果为真的表格。

用简单的英文,从结果集中移除评估为UNKNOWN行。

MSDN有一个很好的关于空值的描述性文章 ,以及它们产生的三个状态逻辑。

简而言之,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 

问题是:
一个未知的人是否相等
(NULL = NULL)
这个问题是没有人可以回答的,所以它根据你的ansi_nulls设置默认为true或false。

然而问题是:
这个未知variables是未知的吗?
这个问题是完全不同的,可以用真实的答案。

nullVariable = null是比较值
nullVariable为null时比较variables的状态

由于NULL表示“未知值”,两个未知值不能相等。

所以,如果我们的逻辑NULL N°1等于NULL N°2,那么我们不得不说:

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

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

在technet中 ,对于空值的工作原理有一个很好的解释。

空意味着未知。

所以布尔expression式

值=空

不计算为false,计算结果为null,但如果这是where子句的最终结果,则不返回任何结果。 这是一个切实可行的方法,因为返回null是很难想象的。

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

如果在我们有一个查询

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

  • 值= 1
  • @参数是null
  • ID = 123
  • @ anotherParam = 123

然后

“value = @ param”的计算结果为null
“@param为null”的计算结果为true
“id = @ anotherParam”的计算结果为true

所以要评估的expression式就变成了

(null或者true)并且是真的

我们可能会想,在这里“null或true”将被评估为null,因此整个expression式变为null,并且行将不会被返回。

事实并非如此。 为什么?

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

所以我们终于有true = true,因此行将被返回。

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

更新:
好吧,只是为了使它完成,我想在这里添加其余的,这相当于上述相当有趣。

“null或false”评估为null,“null和false”评估为false。 🙂

当然,逻辑仍然像以前一样不言自明。

混淆来自于使用NULL产生的间接性(抽象)级别

回到“圣诞树下的东西”类比,“未知的”描述了关于框A中的内容的知识状态。

所以,如果你不知道框A中的内容,你会说它是“未知”,但这并不意味着“未知”就在框内 。 除了未知的东西在框中,可能是某种对象,或者可能没有东西在框中。

同样,如果您不知道框B中的内容,则可以将有关内容的知识状态标记为“未知”。

所以这就是踢球者: 关于框A的知识状态等于你对框B的认识状态 。 (在这两种情况下,你的知识状态是“未知的”或“我不知道盒子里有什么”)。但是盒子的内容可能是平等的。

回到SQL,理想情况下,你应该只能比较值,当你知道他们是什么。 不幸的是,描述缺乏知识的标签存储在单元本身中 ,所以我们试图将其作为一个价值。 但是我们不应该用它作为价值,因为这会导致“当我们不知道框A中的内容和/或我们不知道框B中的内容时,框A的内容等于框B的内容。 (从逻辑上讲,“如果我不知道框A中的内容,如果我不知道框B中的内容,那么框A中的内容=框B中的内容”的含义是错误的。

耶,死马。

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

但是,通过将ANSI_NULLS设置为Off(默认为On),您可以获得该行为。您将可以使用=运算符

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

除了其他精彩的答案:

 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