在 SQL Server 中,如果 where 子句中有 nullParam=NULL,则结果始终为 false。这是违反直觉的,并且给我带来了很多错误。我确实理解 IS NULL 和 IS NOT NULL 关键字...
在 SQL Server 中,如果您 nullParam=NULL
在 where 子句中,它总是计算结果为 false。这是违反直觉的,并导致我犯了很多错误。我确实知道 IS NULL
和 IS NOT NULL
关键字是正确的方法。但为什么 SQL Server 会这样表现呢?
在此我希望澄清我的立场。
那个 NULL = NULL
评估 FALSE
是错误的。 Hacker 和 Mister 正确回答了这个问题 NULL
的评论中写信给我 Scott Ivey :
因为现在是十二月,我们来举一个季节性的例子。我的树下有两件礼物。现在,你告诉我我是否收到了两件相同的东西。
它们可以不同,也可以相同, 你不会知道 。谁知道呢?你邀请了两个互不相识的人,他们都送了你同样的礼物——罕见,但并非不可能 § .
那么问题是:这两个 UNKNOWN 表示相同 (相等,=) 吗?正确答案是:UNKNOWN (即 NULL
)。
此示例旨在证明 \'..( false
或 null
,取决于您的系统)..\' 是一个正确答案 - 它不是, 仅 NULL
在 3VL 中才正确(或者您可以接受给出错误答案的系统?)
正确回答这个问题 必须强调以下两点:
因此我重申:SQL 强迫人们解释相等的反身性是没有任何好处的,它规定:
for any x, x = x
§§ (用简单的英语来说:无论论域如何, “事物”总是等于其自身 )。
.. 在 3VL ( TRUE
, FALSE
, NULL
) 中。人们的期望将符合 2VL ( TRUE
, FALSE
,即使在 SQL 中也对所有其他值有效),即, x = x
对于 x 的任何可能值, TRUE
始终求值为
还要注意,NULL 是有效的“ 非值 ”(正如其辩护者所假装的那样),可以将其分配为关系变量的属性值(??)。因此,它们是每种类型(域)的可接受值,而不仅仅是逻辑表达式的类型。
这 就是我的观点 : NULL
作为价值,它是一个“奇怪的野兽”。毫不委婉地说,我更愿意说: 胡说八道 .
我认为这种表述更加清晰,争议更少——抱歉我的英语水平不佳。
这只是 NULL 的问题之一。如果可能的话,最好完全避免使用它们 。
§ 我们在这里关注的是 价值 ,因此两个呈现 始终 两个不同的物理对象这一事实并不是一个有效的反对意见;如果您不相信,很抱歉,这里不是解释值和“对象”语义之间差异的地方(关系代数从一开始就有值语义 - 参见 Codd 的信息原理;我认为一些 SQL DBMS 实现者甚至不关心通用语义)。
§§ 据我所知,这是一个自古以来就被接受的公理(以某种形式,但总是用 2VL 来解释),正是 因为 它非常直观。3VL(实际上是一类逻辑)是一个较新的发展(但我不确定它是什么时候首次开发的)。
旁注: 如果有人将引入 Bottom , Unit 和 Option 类型作为证明 SQL NULL 的尝试,我将仅在经过非常详细的检查后才会相信,这将展示带有 NULL 的 SQL 实现如何具有健全的类型系统,并最终阐明 NULL(这些“不完全是值的值”)到底是什么。
接下来我将引用一些作者的话。 任何错误或遗漏都可能是我造成的,与原作者无关。
Joe Celko 谈 SQL NULL
我看到 Joe Celko 经常在这个论坛上被引用。显然他是这里一位备受尊敬的作家。所以,我对自己说:“他写了什么关于 SQL NULL 的内容?他如何解释 NULL 的众多问题?”我的一个朋友有一本 Joe Celko 的《SQL for smarties:高级 SQL 编程,第 3 版》 。让我们看看。
首先是目录。最让我印象深刻的是 NULL 被提及的次数以及在各种不同的上下文中:
3.4 算术和 NULL 109
3.5 将值转换为 NULL 或从 NULL 转换 110
3.5.1 NULLIF() 函数 110
6 NULL:SQL 中缺失的数据 185
6.4 比较NULL 190
6.5 NULL 和逻辑 190
6.5.1 子查询谓词中的 NULL 191
6.5.2 标准SQL解决方案 193
6.6 数学和 NULL 193
6.7 函数和空值 193
6.8 NULL 和宿主语言 194
6.9 NULL 的设计建议 195
6.9.1 避免主机程序中的 NULL 值 197
6.10 关于多个NULL值的注释 198
10.1 IS NULL 谓词 241
10.1.1 NULL 的来源 242
...
等等。对我来说,这听起来像是“令人讨厌的特殊情况”。
出于版权原因,我将引用本书中的一些案例,尽量只谈重点。我认为这些引文符合“合理使用”原则,甚至可以刺激人们购买本书 - 所以我希望没有人会抱怨(否则我将需要删除大部分内容,如果不是全部的话)。此外,出于同样的原因,我将避免报告代码片段。对此深表歉意。购买本书以了解数据化推理。
下文括号内为页码。
NOT NULL 约束(11)
最重要的列约束是 NOT NULL,它禁止在列中使用 NULL。请定期使用此约束,并且只有在有充分理由时才将其删除。它将帮助您 在查询数据时 NULL 值
它不是一个值 ;它是一个标记,标记着值可能出现的位置。
又是“有价值但不完全是价值”的废话。其余部分对我来说似乎很合理。
(12)
简而言之,NULL 会导致 SQL 中出现许多不规则的特征,我们将在后面讨论。最好的办法就是记住无法避免 NULL 的情况和规则。
关于 SQL、NULL 和无限:
(104)第 3 章:SQL 中的数值数据
由于多种原因,SQL 没有接受 IEEE 数学模型。
...
如果 SQL 允许使用 IEEE 数学规则,那么我们需要无限的类型转换规则,以及在转换后表示无限精确数值的方法。人们已经对 NULL 感到够烦了,所以我们就不去管它了。
SQL 实现尚未确定 NULL 在特定上下文中的真正含义:
3.6.2 指数函数(116)
问题是当 (x <= 0) 时对数未定义。 一些 SQL 实现 会返回错误消息,一些会返回 NULL 和 DB2/400;版本 3 发行版 1 返回 *NEGINF(“负无穷大”的缩写)作为其结果。
Joe Celko 引用 David McGoveran 和 CJ 的话日期:
6 NULL:SQL 中缺失的数据(185)
在他们的著作 《Sybase 和 SQL Server 指南》 ,David McGoveran 和 CJ Date 表示:“作者认为,NULL 至少是目前在 SQL 中定义和实现的 NULL 带来的麻烦远远大于其价值,应该避免使用;它们表现出非常奇怪和不一致的行为,并且可能成为错误和混乱的根源。(请注意,这些评论和批评适用于任何支持 SQL 样式 NULL 的系统,而不仅仅是 SQL Server。)”
NULL 就像是一种 毒瘾 :
(186/187)
在本书的其余部分, 我将敦促您不要使用它们 ,这似乎自相矛盾,但事实并非如此。将 NULL 视为一种药物;正确使用它,它会对你有用,但滥用它会毁掉一切。 最好的策略是尽可能避免使用 NULL,并在必要时正确使用它们。
我在这里的唯一反对意见是“正确使用它们”,这与特定的实施行为会产生不良影响。
6.5.1 子查询谓词中的 NULL(191/192)
人们忘记了子查询经常隐藏与 NULL 的比较。考虑这两个表:
...
结果将为空。这是 违反直觉的 ,但却是正确的。
(分隔符)
6.5.2 标准SQL解决方案 (193)
SQL-92 通过添加以下形式的新谓词解决了一些 3VL(三值逻辑)问题:
是 [不] 正确 | 错误 | 未知
但是 UNKNOWN 本身就是问题的根源,因此,CJ Date 在下面引用的书中的第 4.5 章“避免 SQL 中的空值” :
- 在任何情况下都不要使用关键字 UNKNOWN。
阅读 “ASIDE” ,链接如下。
6.8 NULL和宿主语言(194)
但是,您应该知道当必须将 NULL 传递给主机程序时如何处理它们。没有一种定义嵌入的标准主机语言支持 NULL,这是避免在数据库架构中使用它们的另一个很好的理由。
(分隔符)
6.9 NULL的设计建议(195)
最好尽可能在所有基表的所有列上声明 NOT NULL 约束。NULL 会让不懂 SQL 的人感到困惑,而且 NULL 的代价很高。
反对意见:NULL 甚至会让熟悉 SQL 的人感到困惑,请参见下文。
(195)
应避免在外键中使用 NULL。SQL 允许这种“疑点利益”关系,但它可能会导致涉及连接的查询中的信息丢失。例如,给定库存中的零件编号代码,该代码被订单表引用为外键,您将无法获取具有 NULL 的零件列表。这是一种强制性关系;您不能订购不存在的零件。
(分隔符)
6.9.1 避免主机程序中使用 NULL(197)
您可以使用一些编程规则来避免将 NULL 放入主机程序的数据库中。
...
- 确定缺失数据对编程和报告的影响: 带有 NULL 的数字列是一个问题,因为使用聚合函数的查询可能会提供误导性的结果。
(分隔符)
(227)
空集的 SUM() 始终为 NULL。使用此技巧时最常见的编程错误之一是编写可能返回多行的查询。如果您没有考虑这一点,您可能会将最后一个例子写成:...
(分隔符)
10.1.1 NULL 的来源(242)
记住 NULL 可能出现的位置很重要。 它们不仅仅是列中的一个可能值 。空集上的聚合函数、OUTER JOIN、带有 NULL 的算术表达式和 OLAP 运算符都返回 NULL。这些构造通常显示为视图中的列。
(分隔符)
(301)
当您尝试将 IN 谓词转换为 EXISTS 谓词时,会发现 NULL 的另一个问题。
(分隔符)
16.3 所有谓词和极值函数(313)
乍一看,这两个谓词在 SQL 中并不相同,这有点违反直觉:
...
但是您必须记住极值函数的规则——它们在返回较大或最小值之前会删除所有 NULL。ALL 谓词不会删除 NULL,因此您可以在结果中获得它们。
(分隔符)
(315)
然而,标准中的定义是否定的,因此 NULL 获得了怀疑的好处。...
如您所见,避免在 UNIQUE 约束中使用 NULL 是一个好主意。
讨论 GROUP BY:
NULL 被视为彼此相等 ,并形成自己的组。然后,每个组都会缩减为新结果表中的一行,以替换旧行。
这意味着对于 GROUP BY 子句,NULL = NULL 不会被计算为 NULL(如在 3VL 中一样),但它会被计算为 TRUE。
SQL标准令人困惑:
ORDER BY 和 NULL(329)
排序键值 NULL 是否被认为大于或小于非 NULL 值是由实现定义的,但是...
... 有一些 SQL 产品可以做到这一点。
1999 年 3 月,Chris Farrar 向他的一位开发人员提出了一个问题,这促使他研究了 SQL 标准中我认为我理解的一部分 。Chris 发现, 一般理解与规范的实际措辞之间存在一些差异 .
等等。我认为 Celko 的回答已经足够了。
SQL NULL 上的 CJ 日期
CJ Date 对 NULL 的态度更加激进:在 SQL 中避免使用 NULL,就是这样。事实上,他的 《SQL 和关系理论:如何编写准确的 SQL 代码 》第 4 章的标题是“没有重复,没有 NULL”,其中包含子章节 “4.4 NULL 有什么问题?” 和“4.5 避免 SQL 中的 NULL”(点击链接:感谢 Google 图书,您可以在线阅读一些页面)。
Fabian Pascal 谈 SQL NULL
摘自《 数据库管理中的实际问题——思考实践者的参考》 (抱歉,网上没有摘录):
10.3 实际意义
10.3.1 SQL NULL
... SQL 存在 3VL 固有的问题以及许多怪癖、复杂性、违反直觉和彻头彻尾的错误 [10, 11];其中包括:
- 聚合函数(例如 SUM()、AVG())忽略 NULL(COUNT() 除外)。
- 没有行的表上的标量表达式错误地计算为 NULL,而不是 0。
- 表达式 \'NULL = NULL\' 的计算结果为 NULL,但在 SQL 中实际上无效;然而 ORDER BY 将 NULL 视为相等(无论它们在“常规”值之前或之后的内容都留给 DBMS 供应商)。
- 表达式 \'x IS NOT NULL\' 不等于 \'NOT(x IS NULL)\',就像 2VL 中的情况一样。
...
所有商业实现的 SQL 方言都遵循这种 3VL 方法,因此,它们不仅存在这些问题,而且 还存在特定的实现问题,这些问题因产品而异 .