空值 (SQL)

空值（Null或NULL）是结构化查询语言中使用的特殊标记，是中对数属性未知或缺失的一种标识，用于指示数据库中不据值。由关系数据库模型的创作者E.F.科德所引入。SQL空值是用来满足真实关系数据库管理系统（RDBMS）中，支持“缺失信息与不适用的信息”的需求。科德还介绍了在数据库理论中使用小写的希腊字母（ω）符号来表示空值。在 SQL中则是以NULL 用于标识空值的保留关键字。SQL null是一个状态，而不是一个值。这种用法与大多数编程语言完全不同，其中引用的空值意味着不指向任何对象。
这不应与0 数值混淆。空值表示缺少值－而与零值不同，与缺乏答案的方式不同，作为“否”的答案。例如，考虑“亚当拥有多少本书？”这个问题，答案可能是“零”（我们知道他没有）或“空白”（我们不知道他拥有多少）。在数据库表格中，回报此问题的列结果，将从没有值（标记为Null）开始，并且在我们确定亚当没有书籍之前，并不会更新为值“零”。
数据库表主键的取值不能为空值。另外，数据库中的统计计算，一般将有空值的数据忽略不计。

历史

EF Codd 在一篇1975年的ACM - SIGMOD FDT报告的论文中提到了空值作为表示关系模型中缺失数据的一种方法。Codd最常被引用的与Null语义相关的论文（如 SQL中所采用的）是他1979年在ACM Transactions on Database Systems上发表的论文，其中他还介绍了他的Relational Model/Tasmania，尽管其他许多提议后面的文章一直很模糊。1979年他的论文2.3节详细描述了空值在做算术运算时散播的语义，以及比较空值时使用三值（三元）逻辑的比较；他还详细说明了对其它集合操作的空值处理（后者当前仍存在争议）。在数据库理论界，Codd最初的建议（1975,1979）现在被称为“Codd tables”。 Codd后来强化了他的要求，即在ComputerWorld杂志上发表的一篇1985年由两部分组成的文章中，所有 RDBMS都支持Null来表示缺失的数据。
在 IBM System R中实现原型后，1986年的SQL标准基本上采用了Codd的建议。虽然 Don Chamberlin认为nulls（和重复行一起）是 SQL最有争议的特性之一，但他辩护SQL中的Nulls设计，并提出了实用的观点，认为这是系统对缺少信息支持最方便的形式，从而使程序员免受许多重复的应用程序级别检查（请参阅semipredicate问题），同时也为数据库设计人员提供了不愿意使用Null的选项; 例如，为了避免众所周知的异常（在本文的语义部分讨论）。Chamberlin还认为，除了提供一些缺失值功能之外，对于空值的实践经验也会导致依赖于空值的其它语言特性，如某些分组构造和外链接。最后，他认为在实践中，Nulls最终也会被用作快速修补现有模式的一种方法，当它需要超出其原始意图的时候，不是为了丢失而是为了不适用的数据编码; 例如，一个数据库如果有每英里加仑的字段，需要快速新增值为电动汽车时。
Codd在其1990年出版的“数据库管理关系模型第2版”中指出，SQL标准所要求的单一Null 类型是不够的，应该用两种不同的Null 类型标记来替代，以表明数据丢失的原因。在 Codd的书中，这两个Null型标记分别被称为“A值”和“I值”，分别表示“缺失但适用”和“缺失但不适用”。 Codd的建议将要求扩展SQL的逻辑系统以适应四值逻辑系统。由于这种额外的复杂性，具有不同定义类型的多个空值概念，尚未在数据库领域广泛接受。尽管如此，它仍然是一个活跃的研究领域，许多论文还在发表。

挑战

由于其相关的三值逻辑（3VL），它在SQL连接中使用的特殊要求，以及聚合函数和SQL分组操作符所需的特殊处理，空值一直是争议的焦点和争议的来源。计算机科学教授Ron van der Meyden将各种问题总结为：“SQL标准中的不一致，意味着不可能将任何直观的逻辑语义归于SQL中的空值处理”。 尽管为解决这些问题提出了各种建议，但候选方案的复杂性阻碍了它们的广泛采用。

空值的散播

由于 Null不是数据值，而是缺少值的标记，因此以数学运算符使用Null会给出未知结果。

算术运算

该结果由空值表示。下例中将Null乘以10将导致Null：
10 * NULL          -- Result is NULL
这可能会有出乎意料的结果。例如，当试图将Null除以零时，平台可返回Null值而不会抛出预期的“除以零－数据异常”。虽然这种行为不是由ISO SQL标准定义的，但许多DBMS供应商都以类似的方式处理这一操作。例如，Oracle，PostgreSQL，MySQL服务器和Microsoft SQL Server平台都会针对以下内容返回空值结果：
NULL / 0

字符串连接

在 SQL中常见的字符串连接操作，在其中一个操作元为Null时也会导致Null。下例演示了使用Null与 SQL 字符串连接运算符|| 返回的Null结果：
'Fish ' || NULL || 'Chips'   -- Result is NULL
对于数据库实现来说并非都是如此。在 Oracle RDBMS中，例如 NULL和空字符串被认为是相同的，因此'Fish'|| NULL || '芯片'字符串连接的结果是'Fish芯片'。

WHERE子句中的未知效应

在数据操纵语言（DML）和查询的比较谓词中，若遇到SQL三值逻辑，该 WHERE子句会导致DML语句仅对那些谓词评估为True的列结果起作用；若是 INSERT，UPDATE 或者 DELETE 等 DML语句，则因为此谓词计算的结果为假、或是未知列，不会运行，并放弃SELECT查询。将 Unknown 解释成False 相同的逻辑结果，是处理空值时会遇到的常见错误，下面的简单例子说明了这个谬误：
SELECT *FROM tWHERE i = NULL;
上例的查询逻辑一定返回零列，因为 i 字段与Null 比较的结果一定是传回“未知”，即使对于那些i 为 Null 的数据列也是如此。未知结果导致SELECT语句立即丢弃每一列。（但在实现中，一些 SQL工具将使用与Null的比较来检索列。）

CASE表达式

SQL提供了两种风格的条件表达式。一为“简单 CASE”并像switch 语句一样操作。另一在标准中称为“搜索 CASE”，并像if ... elseif一样运行。
简单 CASE 表达式使用隐式相等比较，它们的运行和DML的 WHERE 子句处理Null 的规则是相同的。因此，一个简单CASE 表达式无法直接检查Null 的存在。在简单CASE 表达式中检查Null 总会导致结果为未知，如下所示：
SELECT CASE i WHEN NULL THEN 'Is Null'  -- This will never be returned
              WHEN    0 THEN 'Is Zero'  -- This will be returned when i = 0
              WHEN    1 THEN 'Is One'   -- This will be returned when i = 1
              ENDFROM t;
不管 i 列所包含的的值是什么（即使它包含Null），该表达式的计算结果都为“未知” ，'Is Null' 字符串将永远不会返回。
另一方面，“搜索” CASE表达式可以使用谓词IS NULL 和 IS NOT NULL 条件。下例显示如何使用搜索CASE表达式来正确检查Null：
SELECT CASE WHEN i IS NULL THEN 'Null Result'  -- This will be returned when i is NULL
            WHEN     i = 0 THEN 'Zero'         -- This will be returned when i = 0
            WHEN     i = 1 THEN 'One'          -- This will be returned when i = 1
            ENDFROM t;
Oracle的 SQL 方言提供了一个内置函数DECODE，可以用它来代替简单的CASE表达式，并考虑两个相等的空值。
SELECT DECODE(i, NULL, 'Null Result', 0, 'Zero', 1, 'One') FROM t;
如果最后都没有与条件相匹配的结果，所有这些结构将会返回NULL；它们有一个默认的ELSE NULL 子句。

程序扩展中的IF语句

SQL/PSM（SQL预存持续模块）定义 SQL的存储程序扩展，例如 IF 语句。但历史上主要的SQL供应商都包含了他们自己专有的程序扩展。循环和比较的程序扩展在空值比较规则下运行，类似于DML 语句和查询。以下存储程序的片段以ISO SQL 标准格式演示了在IF 语句中使用3VL的 Null 。
IF i = NULL THEN
      SELECT 'Result is True'ELSEIF NOT(i = NULL) THEN
      SELECT 'Result is False'ELSE
      SELECT 'Result is Unknown';
该IF语句仅对那些评估为True 的比较运行操作。对于评估为False 或未知的IF 语句，该语句将控制传递给ELSEIF 子句，最后传递给ELSE 子句。上面代码将因与Null 的比较始终评估为未知，结果一定会是'Result is Unknown' 。

对索引操作的影响

某些SQL产品不可以创建包含NULL 的索引键。例如，PostgreSQL 8.3 版本之前B树索引的文件说明，
B－树可以处理数据的等式和范围查询，这些数据可以按照某种顺序排序。特别是，PostgreSQL查询规划器会在使用< ≤ = ≥ > 这些运算符进行比较时，涉及索引列时考虑使用B－树索引：
< ≤ = ≥ > 等效于这些运算符（如 BETWEEN 和 IN）组合的构造也可以用B－树索引搜索实现。（但请注意，IS NULL 不是等于而且不可索引。）:
在强制唯一性的创建索引运行情况下，字段为NULL值会从索引中被排除，并且不会在NULL之间强制运行唯一性。再次引用PostgreSQL文件：
当索引被宣告是唯一时，将不允许具有相同索引值的多个表行。空值不被视为相等。多列唯一索引只会拒绝所有索引列在两行中相等的情况。
这种作法匹配SQL：2003-标量空值比较的规范。
索引空值的另一种方法涉及按照SQL：2003定义的行为将它们处理为不明显。例如，Microsoft SQL Server文档声明如下：
为了创建索引，NULL比较相等。因此，如果键在多行中为NULL，则无法创建唯一索引或UNIQUE 约束。当选择唯一索引或唯一约束的列时，选择定义为NOT NULL 的列。
这两种索引策略都与SQL：2003定义的Null行为一致。因为索引方法没有被SQL：2003标准明确定义，所以空值的索引策略完全由供应商来设计和实现。

空值处理函数

SQL定义了两个函数来显式处理空值：NULLIF 和 COALESCE。这两个函数都是CASE表达式的缩写。

NULLIF

NULLIF 函数接受两个参数。如果第一个参数等于第二个参数，则 NULLIF 返回 Null。否则，返回第一个参数的值。
NULLIF(value1, value2)
因此，NULLIF是以下CASE表达式的缩写：
CASE WHEN value1 = value2
     THEN
         NULL
     ELSE
         value1END

COALESCE

COALESCE 函数接受参数表表，从列表中返回第一个非Null值：
COALESCE(value1, value2, value3, ...)
COALESCE被定义为以下SQL CASE表达式的简写：
CASE WHEN value1 IS NOT NULL THEN value1
     WHEN value2 IS NOT NULL THEN value2
     WHEN value3 IS NOT NULL THEN value3
     ...
     END
一些 SQL DBMS 供应商实现特定的功能类似COALESCE 函数。某些系统（例如 Transact-SQL）实现为ISNULL函数，或者功能类似COALESCE 的其他函数。

NVL

ORACLE NVL函数接受两个参数。它返回第一个非NULL参数，如果所有参数都是NULL，则返回NULL。
COALESCE 表达式可被转换成等效的NVL表达式这样的：
COALESCE ( val1, ... , val{n} )
变成：
NVL( val1 , NVL( val2 , NVL( val3 , … , NVL ( val{n-1} , val{n} ) … )))
这个函数的用例是在一个表达式中用某一特定值替换NULL，例如 NVL(SALARY, 0)，意为'若薪资字段缺少值，则以0 替换它'。但有一个明显例外，在大多数实现中COALESCE 只评估其参数表表到逹第一个非NULL值，这有几个重要的原因：第一个非NULL 参数之后的参数可能是一个函数，它可能在计算成本上很昂贵、无效、或者可能会产生意料之外的副作用；然而 NVL 将评估参数表表其中的所有参数。