结构化查询语言(Structured Query Language)
在创建新行时,可能会遇到某些列的值未知的情况。
假设我们想存储银行账户信息,但不知道某个账户的余额(balance)。我们可以采取以下几种方法:
- 拒绝整行数据:放弃包括账号、信用额度、利率等在内的所有信息。这种方式不太理想。
- 存储默认值:用默认值代替未知值,但有时无法定义默认值,例如余额为
0
或-1
的银行账户可能是有效的。 - 存储一个标记:标记表明没有存储值。这种方法类似于 NaN(非数字)技术。
关系型数据库管理系统(rDBMS)采用最后一种技术,其标记的意义是“此处未存储任何值”。有时人们会说“存储了 NULL 值”或“存储了 NULL 特殊标记”。
扩展布尔逻辑
假设银行账户表中的某些行在 balance
列存储了 NULL 特殊标记。以下两个条件之一是否成立?
balance >= 0
balance <= 0
答案是:否。这些条件既不是“真”(true),也不是“假”(false),因为我们无法确定 balance
的值。这种情况下,我们需要在布尔逻辑中引入一个第三种状态:未知(unknown)。对于包含 NULL 标记的行,这些条件都会评估为“未知”。
稍后我们将定义布尔运算符(NOT
、AND
、OR
和 =
)在 true
/false
与 unknown
交互时的行为。
检索 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 标记:
- 显式使用 NULL 标记的
INSERT
或UPDATE
命令。SQL 关键字NULL
表示 NULL 特殊标记。 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 NULL
和IS NOT NULL
来检测 NULL。 - 函数如
coalesce
和nullif
简化了 NULL 处理逻辑。 - 不同数据库供应商可能提供额外的 NULL 处理函数,如
isnull()
、ifnull()
或nvl()
,具体含义根据供应商而定。