IS [NOT] DISTINCT FROM 是 SQL 标准中用于安全比较 NULL 的操作符,语义上将 NULL 视为确定值:NULL IS NOT DISTINCT FROM NULL 返回 TRUE,NULL IS DISTINCT FROM 非 NULL 返回 TRUE,彻底解决三值逻辑下 = 和 != 对 NULL 判断失效的问题。

WHERE中比较字段是否变化时,NULL值会让失效
直接用 判断字段是否更新,在遇到 NULL 时会返回 NULL,而 WHERE 条件里 NULL 等价于 FALSE,导致本该触发的逻辑被跳过。比如 OLD.name NEW.name 在 OLD.name 为 'Alice'、NEW.name 为 NULL 时结果是 NULL,整行不匹配。
这不是 bug,是 SQL 三值逻辑的固有行为。所以只要字段可能为 NULL,就别依赖 或 = 做变更检测。
-
IS DISTINCT FROM把 NULL 当作一个确定的值来比较:NULL 和 NULL 相同,NULL 和非 NULL 不同 - 它只适用于标量比较,不能用于数组、JSONB 等复合类型(除非类型支持该操作符)
- 在触发器中必须配合
UPDATE OF使用,否则WHEN子句不生效
在触发器里安全检测字段变更必须用IS DISTINCT FROM
PostgreSQL 的 WHEN 子句只允许在 FOR EACH ROW 触发器中使用,且只能写布尔表达式,不支持函数调用或子查询。此时 IS DISTINCT FROM 是唯一能正确处理 NULL 的原生方案。
错误写法:WHEN (OLD.status NEW.status) —— status 任一为 NULL 就失效
正确写法:WHEN (OLD.status IS DISTINCT FROM NEW.status)
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
- 必须写成
OLD.col IS DISTINCT FROM NEW.col形式,不能反过来(如NEW.col IS DISTINCT FROM OLD.col语义等价,但可读性差) - 多个字段需用
AND连接,例如OLD.a IS DISTINCT FROM NEW.a AND OLD.b IS DISTINCT FROM NEW.b - 性能上比
略低,但这是为正确性付出的合理代价;日常 DML 场景几乎感知不到差异
IS DISTINCT FROM和在NULL场景下的行为差异
假设表 t (id int, name text) 中有一行 (1, NULL):
SELECT * FROM t WHERE name <> 'Alice'; -- 返回空集(因为 NULL <> 'Alice' 结果是 NULL) SELECT * FROM t WHERE name IS DISTINCT FROM 'Alice'; -- 返回该行(NULL 和 'Alice' 确实不同)
再查 (2, 'Alice'):
SELECT * FROM t WHERE name <> 'Alice'; -- 空('Alice' <> 'Alice' 为 FALSE) SELECT * FROM t WHERE name IS DISTINCT FROM 'Alice'; -- 空(相同)
-
IS DISTINCT FROM对非 NULL 值行为等价于,只是多了对 NULL 的明确定义 -
IS NOT DISTINCT FROM等价于=但能正确处理 NULL,即NULL = NULL返回 NULL,而NULL IS NOT DISTINCT FROM NULL返回 TRUE - 不要在索引表达式或 WHERE 高频过滤条件里滥用它——它无法利用普通 B-tree 索引加速,除非你建了函数索引
替代方案:COALESCE不是万能解药
有人用 COALESCE(OLD.col, '') COALESCE(NEW.col, '') 模拟 NULL 安全比较,但这有隐含风险:
- 如果字段本身允许空字符串,
COALESCE(col, '')会把真实空串和 NULL 混淆 - 对数值类型用
COALESCE(col, 0)更危险:0 可能是合法业务值 - 类型转换开销:比如
text字段COALESCE后仍要比较字符串,不如原生操作符快 -
IS DISTINCT FROM是语法级支持,语义清晰、无歧义、无需假设默认值
真正需要兜底的场景极少,绝大多数字段变更检测,直接上 IS DISTINCT FROM 就行——它不是“高级技巧”,而是 NULL 场景下的事实标准写法。

















