在创建新行时,可能会遇到某些列的值未知的情况。

假设我们想存储银行账户信息,但不知道某个账户的余额(balance)。我们可以采取以下几种方法:

  1. 拒绝整行数据:放弃包括账号、信用额度、利率等在内的所有信息。这种方式不太理想。
  2. 存储默认值:用默认值代替未知值,但有时无法定义默认值,例如余额为 0-1 的银行账户可能是有效的。
  3. 存储一个标记:标记表明没有存储值。这种方法类似于 NaN(非数字)技术。

关系型数据库管理系统(rDBMS)采用最后一种技术,其标记的意义是“此处未存储任何值”。有时人们会说“存储了 NULL 值”或“存储了 NULL 特殊标记”。


扩展布尔逻辑

假设银行账户表中的某些行在 balance 列存储了 NULL 特殊标记。以下两个条件之一是否成立?

  • balance >= 0
  • balance <= 0

答案是:否。这些条件既不是“真”(true),也不是“假”(false),因为我们无法确定 balance 的值。这种情况下,我们需要在布尔逻辑中引入一个第三种状态:未知(unknown)。对于包含 NULL 标记的行,这些条件都会评估为“未知”。

稍后我们将定义布尔运算符(NOTANDOR=)在 true/falseunknown 交互时的行为。


检索 NULL 特殊标记

SELECT 命令中,只有当 WHERE 条件评估为 true 时,行才会被返回。如果条件评估为 false 或 unknown,则该行会被排除。

由于条件如 balance >= 0 或其否定 NOT (balance >= 0) 对于包含 NULL 标记的行都评估为 unknown,因此无法通过这些条件检索到 NULL 值。

SQL 提供了特殊的语句 IS NULL,用于检测列中是否包含 NULL 特殊标记:

SELECT ...
FROM   ...
WHERE  <columnname> IS NULL
...    
;

必须使用 IS NULL,因为任何算术操作符(如 ><=!= 等)都无法匹配 NULL 标记。即使条件如 (balance = 0) OR NOT (balance = 0),在传统的布尔逻辑中是一种永真式,但它在含有 NULL 的情况下也无法检索到标记。


示例

我们的测试数据库不包含 NULL 特殊标记,但在 OUTER JOIN 的解释中,我们已经遇到过这种情况。OUTER JOIN 会创建一些列包含 NULL 标记的结果行。

此外,还可以通过以下两种方式生成 NULL 标记:

  1. 显式使用 NULL 标记的 INSERTUPDATE 命令。SQL 关键字 NULL 表示 NULL 特殊标记。
  2. INSERT 命令省略部分列。省略的列将获得 NULL 标记(如果未定义默认值)。

示例:添加测试数据

-- 插入测试数据
INSERT INTO person (id, firstname, lastname)  
VALUES (51, 'Half man', 'Uncomplete');
COMMIT;

-- 检索插入的行。注意 weight 列的默认值为 0,date_of_birth 和 place_of_birth 为 NULL。
SELECT * FROM person WHERE id = 51;

-- 使用 IS NULL 检索行。结果包含 1 行。
SELECT * FROM person WHERE ssn IS NULL;

-- weight 列有值,使用 IS NULL 检索时不会返回任何行。
SELECT * FROM person WHERE weight IS NULL;
SELECT count(*) FROM person WHERE weight IS NULL; -- 返回 0
SELECT count(*) FROM person WHERE weight = 0;     -- 返回 1

-- 使用 IS NOT NULL
SELECT count(*) FROM person WHERE ssn IS NOT NULL;

-- 使用 IS NULL 和 IS NOT NULL 的组合
SELECT count(*)
FROM   person
WHERE  ssn IS NULL
OR     ssn IS NOT NULL; -- 永真式,返回表中所有行

示例:更新列值为 NULL

-- 插入一行完整数据
INSERT INTO person (id, firstname, lastname, date_of_birth, place_of_birth, ssn, weight)
VALUES (52, 'Lyn', 'Mutable', DATE'1951-05-13', 'Anchorage', '078-05-1152', 69);
COMMIT;

-- 将 ssn 列的值设置为 NULL
UPDATE person SET ssn = NULL WHERE id = 52;
COMMIT;

-- 检索含 NULL 的行
SELECT * FROM person WHERE ssn IS NULL; -- 包含 ID 51 和 52 的两行

Coalesce 和类似函数

处理 NULL 时,常需要处理列为空值(NULL)或某个默认值(如 0 或空字符串)的情况。此类条件通常写作:

WHERE (col IS NULL OR col = 0)

为了简化代码,SQL 标准定义了函数 coalesce(<expression_1>, <expression_2>)。如果第一个参数(通常是列名)不为 NULL,则返回该参数值;否则返回第二个参数值。

示例:

-- 检索 ssn 为 NULL 或空白的行
SELECT *
FROM   person
WHERE  coalesce(ssn, ' ') = ' ';

等价于:

WHERE (ssn IS NULL OR ssn = ' ');

函数 coalesce 接受任意数量的参数,并递归地评估它们,直到找到一个非 NULL 的参数为止。

SQL 标准还定义了函数 nullif(<expression_1>, <expression_2>)。如果两个表达式相等,则返回 NULL;否则返回第一个表达式。


总结

  • NULL 标记用于表示“未存储任何值”。
  • 使用 IS NULLIS NOT NULL 来检测 NULL。
  • 函数如 coalescenullif 简化了 NULL 处理逻辑。
  • 不同数据库供应商可能提供额外的 NULL 处理函数,如 isnull()ifnull()nvl(),具体含义根据供应商而定。
最后修改: 2025年01月28日 星期二 12:57