Skip to content

IS NULL 与 NULL 判断(IS DISTINCT FROM) ​

NULL 不是「空字符串」,也不是「0」,而是「未知(unknown)」。它参与任何普通比较运算(=、<>、<、>)时结果都是 NULL,而不是 TRUE 或 FALSE。这决定了判断 NULL 必须使用专门的谓词:IS NULL、IS NOT NULL、IS DISTINCT FROM。本节系统梳理 PostgreSQL 的三值逻辑、NULL 安全比较、排序与聚合中的 NULL 行为,以及 NOT IN 遇 NULL 返回空集这一经典陷阱。

前置知识

阅读本节前,建议先了解:模式匹配(LIKE / SIMILAR TO / POSIX 正则)

基础概念 ​

概念术语说明
三值逻辑Three-Valued Logic逻辑结果有三种:TRUE、FALSE、UNKNOWN(由 NULL 产生)
空值NULL表示「未知」或「缺失」,不等同于空字符串 '' 或数值 0
空值传播NULL Propagation任何含 NULL 的普通算术/比较表达式,结果通常仍为 NULL
空值判断谓词IS NULL / IS NOT NULL唯一可靠的 NULL 检测手段,永远返回 TRUE 或 FALSE
空值安全比较NULL-Safe ComparisonIS DISTINCT FROM / IS NOT DISTINCT FROM,把 NULL 视为可比较的值
布尔断言IS TRUE / IS FALSE / IS UNKNOWNPostgreSQL 特有,把三值逻辑结果收敛为两值布尔
空值排序NULLS FIRST / NULLS LAST显式控制 NULL 在 ORDER BY 中的位置
聚合忽略空值Aggregate Ignores NULLsum、avg、max 等聚合函数自动跳过 NULL 行
唯一索引空值NULL in Unique IndexPostgreSQL 认为多个 NULL 互不相等,故唯一索引允许多行 NULL
检查约束放行NULL in CHECKCHECK 约束中条件结果为 UNKNOWN 时视为通过,即 NULL 被放行

理解 NULL 的关键在于记住一句话:NULL 代表「不知道」。既然不知道,那么「不知道 = 不知道」也无法判断真假,答案只能是「不知道」(UNKNOWN)。PostgreSQL 的一切 NULL 行为都从这个原则推导而来。

sql
-- 三值逻辑的直观体现
SELECT
  NULL = NULL       AS "null_eq_null",     -- NULL
  1 = NULL          AS "one_eq_null",      -- NULL
  NULL IS NULL      AS "is_null",          -- true
  NULL IS NOT NULL  AS "is_not_null",      -- false
  NULL IS DISTINCT FROM NULL AS "distinct"; -- false

语法与用法 ​

NULL 相关谓词一览:

表达式返回类型语义
expr IS NULLboolean表达式为 NULL 时返回 TRUE
expr IS NOT NULLboolean表达式非 NULL 时返回 TRUE
expr IS DISTINCT FROM vboolean二者「不同」(NULL 视为一个确定值)时返回 TRUE
expr IS NOT DISTINCT FROM vboolean二者「相同」(含双方均为 NULL)时返回 TRUE
expr IS TRUEboolean表达式恰好为 TRUE(NULL 返回 FALSE)
expr IS FALSEboolean表达式恰好为 FALSE(NULL 返回 FALSE)
expr IS UNKNOWNboolean表达式为 NULL,即逻辑值为 UNKNOWN
sql
-- 最小可运行示例:在任何库中均可直接执行
SELECT
  1 IS DISTINCT FROM NULL        AS a,  -- true:1 与 NULL 不同
  NULL IS NOT DISTINCT FROM NULL AS b,  -- true:双方都是 NULL,视为相同
  (NULL = NULL) IS UNKNOWN       AS c;  -- true:普通比较结果为 UNKNOWN

详细说明 ​

三值逻辑与真值表 ​

SQL 的逻辑不是二值的,而是三值的。AND、OR、NOT 在引入 NULL 后的完整真值表如下:

AND 真值表:

ANDTRUEFALSENULL
TRUETRUEFALSENULL
FALSEFALSEFALSEFALSE
NULLNULLFALSENULL

OR 真值表:

ORTRUEFALSENULL
TRUETRUETRUETRUE
FALSETRUEFALSENULL
NULLTRUENULLNULL

规律很直观:

  • AND:只要有一边是 FALSE,结果就是 FALSE(FALSE 具有「支配性」,能压过 NULL)。
  • OR:只要有一边是 TRUE,结果就是 TRUE(TRUE 具有支配性)。
  • 其余涉及 NULL 的情形结果都是 NULL。
sql
SELECT
  (TRUE AND NULL)  AS "t_and_null",   -- NULL
  (FALSE AND NULL) AS "f_and_null",   -- false
  (TRUE OR NULL)   AS "t_or_null",    -- true
  (FALSE OR NULL)  AS "f_or_null",    -- NULL
  (NOT NULL)       AS "not_null";     -- NULL

这条规则解释了 WHERE 子句为何「丢掉」NULL 行:WHERE 只保留条件结果为 TRUE 的行,而结果为 FALSE 或 NULL(UNKNOWN)的行都会被过滤掉。

sql
-- 假设 products.price 有 NULL 值
SELECT sku, price FROM products WHERE price > 100;
-- price 为 NULL 的行既不满足 TRUE 也不满足 FALSE,条件结果为 NULL,故被过滤

IS NULL / IS NOT NULL ​

这是判断空值的标准写法,返回一个确定的布尔值,绝不会返回 NULL。

sql
-- 找出所有价格未填写的商品
SELECT sku, name, price
FROM products
WHERE price IS NULL
ORDER BY sku;

-- 找出所有价格已填写的商品(NULL 行被排除)
SELECT sku, name, price
FROM products
WHERE price IS NOT NULL
ORDER BY sku;

常见错误:用 = NULL 或 <> NULL 判断空值。

sql
-- 错误!结果永远为空集,因为 price = NULL 恒为 NULL
SELECT sku FROM products WHERE price = NULL;

-- 正确
SELECT sku FROM products WHERE price IS NULL;

同理,<> NULL、> NULL 等写法也全部是错误的,必须换成 IS NOT NULL / IS NULL。

IS DISTINCT FROM / IS NOT DISTINCT FROM ​

普通比较运算符在遇到 NULL 时结果不确定;IS DISTINCT FROM 系列则把 NULL 当作一个「可比较的确定值」来处理:两个 NULL 被视为相等。

其等价关系可以这样理解:

  • a IS DISTINCT FROM b ⟺ NOT (a IS NOT DISTINCT FROM b)
  • a IS NOT DISTINCT FROM b ⟺ (a = b) IS NOT FALSE(即 a = b 为 TRUE 或 NULL 时都算「不区分」)
sql
SELECT
  1  IS DISTINCT FROM 2     AS a,  -- true
  1  IS DISTINCT FROM 1     AS b,  -- false
  1  IS DISTINCT FROM NULL  AS c,  -- true(普通 1 = NULL 会得 NULL)
  NULL IS DISTINCT FROM NULL AS d; -- false(普通 NULL = NULL 会得 NULL)

典型用途:字段的「变更检测」与「幂等 upsert」。当新旧值都可能为 NULL 时,用 = 无法正确判断,用 IS DISTINCT FROM 才能得到稳定结果。

sql
-- 只更新真正发生变化的行(新旧值都可能为 NULL)
UPDATE products p
SET price = :new_price
WHERE p.sku = :sku
  AND p.price IS DISTINCT FROM :new_price;

IS TRUE / IS FALSE / IS UNKNOWN(PostgreSQL 特有) ​

PostgreSQL 提供了把三值逻辑「收敛」为两值布尔的谓词,常用于 CHECK 约束、部分索引、布尔列查询等场景。

谓词布尔表达式为 TRUE为 FALSE为 NULL
expr IS TRUETRUEFALSEFALSE
expr IS FALSEFALSETRUEFALSE
expr IS UNKNOWNFALSEFALSETRUE
expr IS NOT TRUEFALSETRUETRUE
expr IS NOT FALSETRUEFALSETRUE
sql
-- 只保留「明确为真」的行;NULL 行会被排除(与普通 WHERE 行为一致)
SELECT * FROM products WHERE (price > 100) IS TRUE;

-- 只保留「明确为假」的行;NULL 行会被排除
SELECT * FROM products WHERE (price > 100) IS FALSE;

-- 专门找出「无法判定」的行(price 为 NULL 导致条件为 UNKNOWN)
SELECT * FROM products WHERE (price > 100) IS UNKNOWN;

= 与 IS DISTINCT FROM 的执行计划差异 ​

IS DISTINCT FROM 因为要额外处理 NULL,无法直接套用普通 B-tree 索引的等值或范围扫描策略。对于 IS NOT DISTINCT FROM,PostgreSQL 可以把 NULL 也纳入索引范围(相当于 = 值 OR IS NULL),但代价是可能退化为 Bitmap 扫描。

sql
-- 普通等值:可以走索引
EXPLAIN (ANALYZE, COSTS OFF)
SELECT * FROM products WHERE sku = 'SKU-1001';

-- 空值安全比较:可能走 Bitmap 扫描(含 NULL 分支)
EXPLAIN (ANALYZE, COSTS OFF)
SELECT * FROM products WHERE sku IS NOT DISTINCT FROM 'SKU-1001';

在数据量大、且确定列上没有 NULL 的情况下,用普通 = 更利于优化器生成高效计划;只有确实需要处理 NULL 时才使用 IS NOT DISTINCT FROM。

NULL 在 CHECK 约束中的放行行为 ​

CHECK 约束的判定规则是:只有条件结果为 FALSE 时才拒绝写入;结果为 TRUE 或 NULL(UNKNOWN)都视为通过。这意味着一旦条件里出现 NULL,约束就形同虚设。

sql
-- 危险!price 为 NULL 时条件为 NULL,约束不生效
CREATE TABLE products_bad (
  sku   text PRIMARY KEY,
  price numeric CHECK (price > 0)
);

-- 插入 price 为 NULL 的行会成功,绕过约束
INSERT INTO products_bad (sku, price) VALUES ('SKU-9001', NULL);  -- 成功!

-- 正确:显式加上 NOT NULL 或把 NULL 也视为非法
CREATE TABLE products_ok (
  sku   text PRIMARY KEY,
  price numeric NOT NULL CHECK (price > 0)
);

如果要允许 NULL,但又想校验「非 NULL 时必须满足条件」,可写成 price IS NULL OR price > 0。

NULL 在唯一索引中的行为 ​

SQL 标准规定:每个 NULL 都与其它 NULL「不相等」,因此唯一索引允许多行 NULL。PostgreSQL 严格遵守这一行为。

sql
CREATE TABLE users (
  id    integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  email text UNIQUE,          -- email 可为 NULL,且允许多行 NULL
  phone text
);

-- 以下多行 email 为 NULL 都会插入成功
INSERT INTO users (email) VALUES (NULL), (NULL), (NULL);  -- 3 行均成功

-- 非 NULL 的重复值则会被拒绝
INSERT INTO users (email) VALUES ('a@example.com');
INSERT INTO users (email) VALUES ('a@example.com');  -- 错误!违反唯一约束

如果业务上希望「NULL 也只能出现一次」,可以用部分唯一索引配合表达式:

sql
-- PostgreSQL 15+ 起可直接在唯一约束上声明 NULLS NOT DISTINCT
CREATE TABLE users_strict (
  id    integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  email text UNIQUE NULLS NOT DISTINCT   -- 多个 NULL 也会被视为重复
);

NULL 排序:NULLS FIRST / NULLS LAST ​

PostgreSQL 默认认为 NULL 是「最大」的值:

  • ORDER BY col ASC 时 NULL 排在最后;
  • ORDER BY col DESC 时 NULL 排在最前。

用 NULLS FIRST / NULLS LAST 可以显式覆盖默认行为。

sql
-- 默认 ASC:NULL 在最后
SELECT sku, price FROM products ORDER BY price ASC;

-- 显式让 NULL 排最前
SELECT sku, price FROM products ORDER BY price ASC NULLS FIRST;

-- 想让 NULL 排最后(无论升降序)
SELECT sku, price FROM products ORDER BY price DESC NULLS LAST;

NULLS FIRST / NULLS LAST 也可以用在本节介绍的 IS NULL 排序技巧上,或配合索引使用:

sql
-- 让「已填写价格」的商品优先展示
SELECT sku, price FROM products ORDER BY (price IS NULL), price;

聚合函数忽略 NULL ​

除 count(*) 外,所有标准聚合函数(sum、avg、min、max、array_agg 等)都会跳过 NULL 行,这一点极易与 count(*) 混淆。

写法语义
count(*)统计所有行数(含全 NULL 行)
count(col)统计 col 非 NULL 的行数
count(DISTINCT col)统计 col 去重后的非 NULL 值个数
sum(col)忽略 NULL 求和;若全为 NULL 则返回 NULL(不是 0)
avg(col)忽略 NULL 求平均,分母是非 NULL 的个数
sql
SELECT
  count(*)          AS total_rows,      -- 所有行
  count(price)      AS priced_rows,     -- price 非 NULL 的行
  sum(price)        AS total_price,     -- 忽略 NULL
  avg(price)        AS avg_price,       -- 分母为非 NULL 行数
  coalesce(sum(price), 0) AS total_or_zero;  -- 全 NULL 时兜底为 0

NOT IN 遇 NULL 返回空集的经典陷阱 ​

这是 SQL 中最著名的坑之一。NOT IN (子查询) 在子查询结果含 NULL 时,会整体返回空集,因为 x <> NULL 恒为 NULL,AND 串起来后整条条件无法为 TRUE。

sql
-- 假设 orders.product_id 中有 NULL
-- 陷阱:只要子查询里出现一个 NULL,结果就变成空集
SELECT * FROM products
WHERE id NOT IN (SELECT product_id FROM orders);  -- 可能返回 0 行!

-- 方案 A:过滤掉子查询中的 NULL
SELECT * FROM products
WHERE id NOT IN (SELECT product_id FROM orders WHERE product_id IS NOT NULL);

-- 方案 B:改用 NOT EXISTS(推荐,语义清晰且天然处理 NULL)
SELECT * FROM products p
WHERE NOT EXISTS (
  SELECT 1 FROM orders o WHERE o.product_id = p.id
);

IN(正匹配)遇到 NULL 时通常不受影响:x IN (1, NULL) 只要 x = 1 就为 TRUE,只有 x 不在列表中且列表含 NULL 时结果才是 NULL,最终同样会被 WHERE 滤掉。

COALESCE 兜底与 NULL 的转换 ​

用 coalesce() 可以把 NULL 替换为默认值,从而「消解」NULL 带来的三值逻辑问题。注意它只是查询层的兜底,并不改变底层数据。

sql
SELECT
  sku,
  coalesce(price, 0)               AS price_or_zero,
  coalesce(description, '暂无描述') AS description_safe
FROM products;

-- 排序时把 NULL 当作一个极小值处理
SELECT sku, coalesce(price, -1) AS price_key FROM products ORDER BY price_key;

空字符串不是 NULL ​

从别的系统或语言迁移过来时,最容易把二者混淆。PostgreSQL 中 '' 是一个确定的值,'' IS NULL 为 FALSE。

sql
SELECT
  '' IS NULL        AS empty_is_null,      -- false
  '' = ''           AS empty_eq_empty,     -- true
  length('')        AS empty_length,       -- 0
  nullif('', '')    IS NULL AS nullif_demo; -- true(nullif 把相等值转成 NULL)

在数据清洗中,常需要把「空字符串」统一为 NULL:

sql
UPDATE products
SET description = NULL
WHERE description = '';   -- 空串归一为 NULL

实战示例 ​

步骤 1:建表并造数据 ​

sql
-- 统一的示例库
CREATE DATABASE demo;

DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS products;

CREATE TABLE products (
  id          integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  sku         text    NOT NULL UNIQUE,
  name        text    NOT NULL,
  price       numeric(10, 2),               -- 允许 NULL,表示价格待定
  description text,                         -- 允许 NULL
  released_on date                          -- 允许 NULL,表示未上架
);

CREATE TABLE orders (
  id         integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  order_no   text NOT NULL,
  product_id integer,                       -- 允许 NULL,表示已删除商品
  qty        integer,
  amount     numeric(10, 2),
  note       text
);

INSERT INTO products (sku, name, price, description, released_on) VALUES
  ('SKU-1001', '27 英寸显示器', 1299.00, '2K 分辨率',     '2025-01-10'),
  ('SKU-1002', '机械键盘',       399.00, '青轴',          '2025-02-01'),
  ('SKU-1003', '无线鼠标',       159.00, NULL,            '2025-02-15'),
  ('SKU-1004', 'HDMI 线缆',      NULL,   '2 米长',        '2025-03-01'),
  ('SKU-1005', '显卡支架',       NULL,   NULL,            NULL),
  ('SKU-1006', 'USB 集线器',     89.00,  '7 口',          '2025-03-20');

INSERT INTO orders (order_no, product_id, qty, amount, note) VALUES
  ('SO-2001', 1,    2, 2598.00, NULL),
  ('SO-2002', 2,    1, 399.00,  '加急'),
  ('SO-2003', NULL, 1, 159.00,  NULL),   -- 商品已被删除
  ('SO-2004', 3,    3, 477.00,  NULL),
  ('SO-2005', NULL, 2, 178.00,  '补发');

步骤 2:三值逻辑与 IS NULL 基础查询 ​

sql
-- 找出价格未定的商品
SELECT sku, name, price
FROM products
WHERE price IS NULL
ORDER BY sku;
text
  sku   |    name    | price
--------+------------+-------
 SKU-1004 | HDMI 线缆  |
 SKU-1005 | 显卡支架   |
(2 rows)
sql
-- 统计「有价格」与「无价格」的商品数量
SELECT
  count(*)               AS total,
  count(price)           AS priced,
  count(*) - count(price) AS unpriced
FROM products;
text
 total | priced | unpriced
-------+--------+----------
     6 |      4 |        2
(1 row)

步骤 3:IS DISTINCT FROM 做变更检测 ​

sql
-- 模拟一次价格更新:只有真正变化的行才会被更新
-- 对 SKU-1004 而言,原值为 NULL,新值 299.00,属于「不同」,需更新
BEGIN;

UPDATE products
SET price = 299.00
WHERE sku = 'SKU-1004'
  AND price IS DISTINCT FROM 299.00;

SELECT sku, price FROM products WHERE sku = 'SKU-1004';

ROLLBACK;   -- 仅演示,回滚以保持数据一致
text
  sku    | price
---------+--------
 SKU-1004 | 299.00
(1 row)

对比用普通 = 的后果:当原值为 NULL 时,price <> 299.00 结果为 NULL,条件不成立,该行会被静默跳过,导致更新丢失。

步骤 4:NOT IN 陷阱与 NOT EXISTS 修复 ​

sql
-- 陷阱:orders.product_id 含 NULL,NOT IN 会让整个查询返回空集
SELECT id, sku, name
FROM products
WHERE id NOT IN (SELECT product_id FROM orders);
text
 id | sku | name
----+-----+------
(0 rows)
sql
-- 修复方案 A:在子查询中排除 NULL
SELECT id, sku, name
FROM products
WHERE id NOT IN (SELECT product_id FROM orders WHERE product_id IS NOT NULL)
ORDER BY id;
text
 id |   sku    |     name
----+----------+--------------
  4 | SKU-1004 | HDMI 线缆
  5 | SKU-1005 | 显卡支架
  6 | SKU-1006 | USB 集线器
(3 rows)
sql
-- 修复方案 B(推荐):NOT EXISTS
SELECT p.id, p.sku, p.name
FROM products p
WHERE NOT EXISTS (
  SELECT 1 FROM orders o WHERE o.product_id = p.id
)
ORDER BY p.id;
text
 id |   sku    |     name
----+----------+--------------
  4 | SKU-1004 | HDMI 线缆
  5 | SKU-1005 | 显卡支架
  6 | SKU-1006 | USB 集线器
(3 rows)

步骤 5:聚合中的 NULL 与排序控制 ​

sql
-- 聚合忽略 NULL:sum 与 avg 都跳过 NULL,count(*) 不跳
SELECT
  count(*)               AS order_rows,
  count(product_id)      AS with_product,
  count(note)            AS with_note,
  sum(amount)            AS total_amount,
  round(avg(amount), 2)  AS avg_amount
FROM orders;
text
 order_rows | with_product | with_note | total_amount | avg_amount
------------+--------------+-----------+--------------+------------
          5 |            3 |         2 |      3810.00 |     762.00
(1 row)
sql
-- 排序:NULL 默认在 ASC 时排最后
SELECT order_no, note FROM orders ORDER BY note ASC;
text
 order_no | note
----------+------
 SO-2002  | 加急
 SO-2005  | 补发
 SO-2001  |
 SO-2003  |
 SO-2004  |
(5 rows)
sql
-- 显式让 NULL 排最前
SELECT order_no, note FROM orders ORDER BY note ASC NULLS FIRST;
text
 order_no | note
----------+------
 SO-2001  |
 SO-2003  |
 SO-2004  |
 SO-2002  | 加急
 SO-2005  | 补发
(5 rows)

步骤 6:NULL 与唯一索引、CHECK 约束 ​

sql
-- 唯一索引允许多行 NULL
INSERT INTO products (sku, name, price, description, released_on)
VALUES
  ('SKU-1007', '散热硅脂', NULL, NULL, NULL),
  ('SKU-1008', '理线带',   NULL, NULL, NULL);

SELECT count(*) AS null_price_rows FROM products WHERE price IS NULL;
text
 null_price_rows
-----------------
               4
(1 row)
sql
-- CHECK 约束对 NULL 放行(危险示例)
CREATE TABLE demo_check_bad (
  sku   text,
  price numeric CHECK (price > 0)
);
INSERT INTO demo_check_bad (sku, price) VALUES ('X-1', NULL) RETURNING sku, price;
text
 sku | price
-----+-------
 X-1 |
(1 row)

注意事项 ​

  • 不要用 = NULL / <> NULL 判断空值。 结果恒为 NULL,条件不成立。必须用 IS NULL / IS NOT NULL。
sql
-- 错误!
SELECT * FROM products WHERE price = NULL;
  • NOT IN (子查询) 前务必确认子查询无 NULL。 存在 NULL 时整个查询返回空集,是线上事故高发点。
sql
-- 不推荐
SELECT * FROM products WHERE id NOT IN (SELECT product_id FROM orders);
-- 推荐
SELECT * FROM products WHERE NOT EXISTS (
  SELECT 1 FROM orders o WHERE o.product_id = products.id
);
  • CHECK 约束遇 NULL 会放行。 想让约束真正生效,需配合 NOT NULL,或把 NULL 显式纳入判断。
sql
-- 不推荐:NULL 可绕过
price numeric CHECK (price > 0)
-- 推荐:显式处理 NULL
price numeric CHECK (price IS NULL OR price > 0)
  • count(*) 与 count(col) 结果通常不同。 前者统计行数,后者只统计非 NULL 值,混用会导致口径错误。

  • sum / avg 在全为 NULL 时返回 NULL,而不是 0。 需要 0 时必须用 coalesce(sum(col), 0)。

  • avg 的分母是非 NULL 行数,不是总行数。用 NULL 表示「缺失」时,这个行为可能与你期望的「按全体行数平均」不一致。

  • IS TRUE / IS FALSE 会把 NULL 归入 FALSE。 若要保留 NULL 行,应改用 IS NOT FALSE 或 IS UNKNOWN。

  • IS DISTINCT FROM 会削弱索引可用性。 能确定无 NULL 时优先用普通 =,避免优化器退化为顺序扫描。

最佳实践 ​

  1. 字段语义为「必填」时一律加 NOT NULL,把空值问题在建模阶段消灭掉,这比在查询层反复兜底划算得多。
sql
CREATE TABLE orders (
  order_no  text    NOT NULL,
  amount    numeric NOT NULL DEFAULT 0
);
  1. 区分「未知」与「不适用」。若 NULL 表达的是「不适用」,考虑用明确的枚举值或独立布尔列代替,避免三值逻辑带来的歧义。

  2. 反连接优先用 NOT EXISTS,它天然正确处理 NULL,且语义比 NOT IN 更贴合「不存在匹配行」的业务含义。

sql
SELECT p.* FROM products p
WHERE NOT EXISTS (SELECT 1 FROM order_items i WHERE i.product_id = p.id);
  1. 变更检测用 IS DISTINCT FROM,避免新旧值为 NULL 时更新被静默跳过。
sql
UPDATE products SET price = :new
WHERE sku = :sku AND price IS DISTINCT FROM :new;
  1. 聚合结果统一用 coalesce 兜底,防止前端拿到 NULL 后渲染异常。
sql
SELECT sku, coalesce(sum(amount), 0) AS total FROM orders GROUP BY sku;
  1. 排序涉及可空列时显式声明 NULLS FIRST / NULLS LAST,不要依赖默认行为,以免升级或改写 SQL 后顺序变化。
sql
SELECT * FROM products ORDER BY released_on DESC NULLS LAST;
  1. 数据清洗时把空字符串统一归一为 NULL,并在应用层同时约定「只写 NULL,不写空串」。
sql
UPDATE products SET description = NULL WHERE description = '';
  1. 在 CHECK 约束里显式处理 NULL,或直接把列声明为 NOT NULL,避免约束被 NULL 绕过。

  2. 用 IS UNKNOWN 调试三值逻辑问题。当查询结果「数量不对」时,先怀疑某个条件因为 NULL 变成了 UNKNOWN。

sql
-- 定位因 NULL 而无判定结果的行
SELECT * FROM products WHERE (price > 100) IS UNKNOWN;

下一节

继续学习:BETWEEN / IN / ANY / ALL

参考链接 ​