空值安全比较
普通等号遇到空值得到的是未知,不是成立也不是不成立,因此比较两个可能为空的列时用等号会漏掉「两个都是空」的行。需要把空值当作一个可比较的值时,要用空值安全的比较写法:MySQL 提供了专用运算符,结果恒为零或一;标准 SQL 与 PostgreSQL 用专门的判定表达式,读起来更接近自然语言。这类比较常出现在去重、版本比对、可空连接键匹配的场景里。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| MySQL 写法 | 空值安全运算符,返回零或一,不会返回未知 |
| PostgreSQL 写法 | 判定表达式的两种形式,分别表示相等与不等 |
| 用途 | 匹配可空连接键、按整行去重、版本比对 |
| 语义差别 | 空值与空值在这种比较下被视为相等 |
典型使用场景
- 可空连接键匹配:两侧都可能为空时仍希望匹配上
- 数据比对:找出两行内容不同的记录,包括空值与值之间的差异
- 去重判断:把空值当成一个确定的取值参与去重
- 变更检测:比较新旧快照的字段是否真的不同
- 同步任务:判断源端与目标端的字段值是否一致
两库写法与结果
两库写法与结果
-- MySQL:空值安全运算符,结果恒为 0 或 1
SELECT NULL <=> NULL, -- 1,两个空被视为相等
1 <=> NULL, -- 0,值与非空不相等
'a' <=> 'a'; -- 1
-- PostgreSQL:判定表达式
SELECT NULL IS NOT DISTINCT FROM NULL; -- true,表示两者不区分
SELECT 1 IS DISTINCT FROM NULL; -- true,表示两者有区别
SELECT 1 IS DISTINCT FROM 1; -- false
-- 典型用法:可空键做连接
SELECT a.id, b.remark
FROM a
LEFT JOIN b ON a.code IS NOT DISTINCT FROM b.code; -- 空值也能匹配上
-- 数据比对:找出两版内容确实不同的记录
SELECT id FROM v1 JOIN v2 USING (id)
WHERE v1.val IS DISTINCT FROM v2.val;比较结果对照
| 左值 | 右值 | 普通等号 | 空值安全比较 |
|---|---|---|---|
| 1 | 1 | 成立 | 成立 |
| 1 | 2 | 不成立 | 不成立 |
| 1 | 空值 | 未知 | 不成立 |
| 空值 | 空值 | 未知 | 成立 |
| 空值 | 空值(取不等形式) | 未知 | 不成立 |
把空值安全比较用在连接条件上时,空值行会互相匹配上。这在按编号对齐的数据里通常是错的,只有业务确实需要「缺失值也视为同组」时才使用。
空值安全运算符是 MySQL 方言,换库时要改写成标准的判定表达式。写数据同步逻辑时统一用标准写法,迁移时就不需要逐条检查。