0Pricing
Coding Interview Prep · 课时

IS NULL、IS NOT NULL 与 NULL 安全相等

正确测试 NULL,并掌握各方言中的 NULL 安全运算符

IS NULL、IS NOT NULL 与 NULL 安全相等 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。

以正确方式检查 NULL

上一课证明了不能使用 = 查找 NULL。那么,实际应该如何检查 NULL 呢?请使用专用谓词 IS NULL 和 IS NOT NULL。

这是检查缺失值的唯一正确且可移植的方式,面试官每次看到 col = NULL 都会判定它是错误的。

本课将介绍 IS NULL、IS NOT NULL、IS DISTINCT FROM 系列,以及特定数据库方言的 NULL 安全等值运算符。了解不同数据库之间的差异,是体现资深水平的重要信号。

IS NULL 和 IS NOT NULL

当值为 NULL 时,IS NULL 返回 TRUE,否则返回 FALSE。关键在于,它永远不会返回 UNKNOWN,因此可以安全地直接用于 WHERE。

IS NOT NULL 是它的完全补集:对于任何实际值返回 TRUE,对于 NULL 返回 FALSE。

这些谓词是处理 NULL 的主力工具。它们属于标准结构化查询语言,在 MySQL、Postgres、SQL Server、Oracle 和 SQLite 中的行为完全一致。

-- Find employees with no recorded bonus
SELECT name FROM employees WHERE bonus IS NULL;

-- Find employees that do have a bonus
SELECT name FROM employees WHERE bonus IS NOT NULL;

为什么列 = NULL 总是错误

这是面试中必考的陷阱:候选人写出 WHERE bonus = NULL,以为这样能找出缺失的奖金。结果是零行。

请记住三值逻辑:对于每一行,包括值为 NULL 的行,bonus = NULL 都是 UNKNOWN,因为没有任何值等于未知值。WHERE 只保留 TRUE,因此没有任何行能匹配。

某些数据库在非标准模式下会悄悄将 = NULL 改写为 IS NULL,但您绝不能依赖这种行为。请始终显式写出 IS NULL。

-- WRONG: returns zero rows, bonus = NULL is UNKNOWN for all
SELECT name FROM employees WHERE bonus = NULL;

-- RIGHT:
SELECT name FROM employees WHERE bonus IS NULL;

统计 NULL 值与非 NULL 值

分析人员经常需要执行数据质量审计:某一列的数据有多完整?将 IS NULL 与 COUNT 结合起来,即可报告缺失值。

请注意二者的区别:COUNT(*) 统计每一行,而 COUNT(bonus) 只统计非 NULL 的奖金。二者之差就等于 NULL 的数量,我们将在聚合课程中再次介绍这一事实。

SELECT
  COUNT(*)                                AS total_rows,
  COUNT(bonus)                            AS with_bonus,
  COUNT(*) - COUNT(bonus)                 AS missing_bonus,
  SUM(CASE WHEN bonus IS NULL THEN 1 ELSE 0 END) AS missing_check
FROM employees;

NULL 安全等值比较所解决的问题

假设您希望匹配两列,并将“两者都是 NULL”视为匹配。普通的 a = b 会失败:当两者都是 NULL 时,结果是 UNKNOWN,因此这对值会被排除,尽管直觉上它们是“相同的”。

在比较旧行和新行以检测变化,或根据可选列进行连接时,都会遇到这种情况。您需要一种比较方式,使NULL 等于 NULL 时结果为 TRUE,而NULL 与某个值比较时结果为 FALSE。这正是 NULL 安全等值比较提供的能力。

-- Goal: change-detection where two NULLs count as equal
-- Plain equality fails when both sides are NULL:
--   NULL = NULL -> UNKNOWN (treated as not-equal)
SELECT * FROM old_t o JOIN new_t n ON o.id = n.id
WHERE o.note = n.note;  -- misses rows where both notes are NULL

IS DISTINCT FROM(标准结构化查询语言)

符合 ANSI 标准的 NULL 安全比较是 IS DISTINCT FROM,其逆运算是 IS NOT DISTINCT FROM。Postgres、SQL Server(2022 及更高版本)以及其他数据库支持它们。

  • a IS NOT DISTINCT FROM b 表示“相等,其中 NULL = NULL 也算相等”。
  • a IS DISTINCT FROM b 表示“不同,将 NULL 当作普通值处理”。

它们始终返回 TRUE 或 FALSE,从不返回 UNKNOWN,因此在任何需要谓词的地方都可以安全使用。

-- TRUE when notes match, including both NULL
SELECT * FROM old_t o JOIN new_t n ON o.id = n.id
WHERE o.note IS NOT DISTINCT FROM n.note;

-- TRUE when notes differ (NULL vs value counts as different)
SELECT * FROM old_t o JOIN new_t n ON o.id = n.id
WHERE o.note IS DISTINCT FROM n.note;

MySQL 的 <=> 运算符

MySQL 提供了一种简洁的 NULL 安全等值运算符,写作 <=>(太空船运算符)。

a <=> b 在两侧相等或两侧都是 NULL 时返回 1(TRUE),否则返回 0(FALSE)。它相当于 MySQL 中的 IS NOT DISTINCT FROM。

如果面试官特别询问 MySQL 中的 NULL 安全匹配,这就是惯用答案。

-- MySQL: 1 when both equal or both NULL
SELECT (NULL <=> NULL) AS both_null,   -- 1
       (NULL <=> 5)    AS null_vs_val, -- 0
       (5 <=> 5)       AS val_eq;      -- 1

SELECT * FROM old_t o JOIN new_t n ON o.id = n.id
WHERE o.note <=> n.note;

跨方言速查表

面试官会认可了解可移植性边界的候选人。下面是 NULL 安全相等比较对照表:

  • ANSI / Postgres / SQL Server 2022+: IS NOT DISTINCT FROM
  • MySQL / MariaDB: <=>
  • SQLite: IS 和 IS NOT 可用作 NULL 安全相等比较
  • Oracle:没有原生运算符;可以使用 DECODE(a, b, 1, 0) = 1 或 COALESCE 技巧进行模拟

如果不确定所用的数据库引擎,请改用下一节所示的可移植手动写法。

-- SQLite NULL-safe equality
SELECT * FROM t WHERE a IS b;      -- TRUE when both NULL
SELECT * FROM t WHERE a IS NOT b;  -- complement

可移植的手动 NULL 安全匹配

如果没有可用的原生运算符,您可以使用基本操作构建 NULL 安全的相等比较。这个可移植模式将普通相等比较与明确的两者均为 NULL 子句结合起来。

可以这样理解:“它们相等,或者它们都缺失。”这适用于所有数据库,因此当面试官没有明确指定方言时,这是很好的回答。

SELECT *
FROM old_t o JOIN new_t n ON o.id = n.id
WHERE (o.note = n.note)
   OR (o.note IS NULL AND n.note IS NULL);

-- Alternative using COALESCE with a sentinel that
-- cannot occur in real data:
-- WHERE COALESCE(o.note, '##NULL##') = COALESCE(n.note, '##NULL##')

深入示例:NULL 安全的 JOIN 键

一个现实中的陷阱是:使用可为 NULL 的键进行连接。如果两侧的 region 都可以为 NULL,普通等值连接会悄悄丢弃这些配对,因为 NULL = NULL 的结果是 UNKNOWN。

如果业务规则是“没有区域的行仍应与另一侧没有区域的行匹配”,您必须使 JOIN 条件具备 NULL 安全性。在面试中请明确说出这一假设,然后选择与数据库引擎相匹配的运算符。

-- Postgres / ANSI: match including both-NULL regions
SELECT a.id, b.id
FROM table_a a
JOIN table_b b
  ON a.region IS NOT DISTINCT FROM b.region;

-- MySQL equivalent: ON a.region <=> b.region

面试要点

要清晰应对任何 NULL 测试问题:

  • 始终使用 IS NULL / IS NOT NULL;绝不要使用 = NULL。
  • 这些谓词只返回 TRUE 或 FALSE,因此可以安全地用于 WHERE。
  • 若要实现“NULL 等于 NULL”的匹配,请使用 IS NOT DISTINCT FROM(ANSI)或 <=>(MySQL)。
  • 说明您针对的是哪种方言;不确定时,提供可移植的 OR 子句回退方案。

同时说出标准运算符和厂商运算符,能展现面试筛选者会注意到的知识广度。

快速检查

请选择正确的 NULL 安全比较。

回顾

现在您已经可以正确测试 NULL:

  • IS NULL / IS NOT NULL 是唯一正确且可移植的 NULL 测试;它们永远不会返回 UNKNOWN。
  • col = NULL 始终返回零行,这是一个经典的面试陷阱。
  • NULL 安全相等比较会将两个 NULL 视为相等:IS NOT DISTINCT FROM(ANSI/Postgres)、<=>(MySQL)、IS(SQLite)。
  • 如果不存在相应运算符,请使用 (a = b) OR (a IS NULL AND b IS NULL)。

下一步:使用 COALESCE、NULLIF 以及 ISNULL 等厂商函数为 NULL 提供默认值。

常见问题解答

「IS NULL、IS NOT NULL 与 NULL 安全相等」课时是免费的吗?

是的 — 「IS NULL、IS NOT NULL 与 NULL 安全相等」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。

「IS NULL、IS NOT NULL 与 NULL 安全相等」这节课中我会学到什么?

正确测试 NULL,并掌握各方言中的 NULL 安全运算符 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Coding Interview Prep 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「IS NULL、IS NOT NULL 与 NULL 安全相等」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Coding Interview Prep 课中编写并运行代码吗?

能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 三值逻辑与 UNKNOWN
  2. IS NULL、IS NOT NULL 与 NULL 安全相等
  3. COALESCE、NULLIF 与 ISNULL
  4. 汇总、连接和 DISTINCT 中的 NULL
← 返回 Coding Interview Prep