NULL 比较为什么会漏判
SCD 的变更检测靠逐列比较属性值,而普通等值比较遇到 NULL 返回的是未知而不是真:当属性从空值变成有值,或者两行都为空时,普通比较无法判定,变更就被漏掉。各引擎的 NULL 安全比较语法不同,跨引擎迁移时必须逐条复核。
基本信息
| MySQL | a <=> b 做 NULL 安全等值比较 |
|---|---|
| PostgreSQL | a IS NOT DISTINCT FROM b |
| Spark SQL | 同时支持 <=> 与 IS NOT DISTINCT FROM |
| Hive | 旧版本无专用运算符,用 coalesce 兜底 |
| 替代做法 | 入维前把空值规整为「未知」占位 |
典型使用场景
- SCD2 变更检测:比较跟踪列判断是否需要产生新版本
- 指纹计算:拼接前统一处理空值,避免指纹不稳定
- 跨引擎迁移:从 Hive 迁到 Spark 或 Doris 时复核比较语义
- 数据校验:比对两表差异时避免因空值被误判为不同
- 增量同步:空值处理错误会导致每次都全量同步
NULL 安全比较
NULL 安全比较
-- 找出 name 或 phone 实际发生变化的当前行(MySQL 8 语法)
SELECT d.customer_id
FROM dim_customer d
JOIN stg_customer s ON d.customer_id = s.customer_id
WHERE d.is_current = 1
AND NOT (d.name <=> s.name)
AND NOT (d.phone <=> s.phone);
-- 通用写法:把空值规整为占位符后比较
-- WHERE coalesce(d.name, '#N/A') <> coalesce(s.name, '#N/A')各引擎写法
| 引擎 | NULL 安全写法 | 备注 |
|---|---|---|
| MySQL | a <=> b | MySQL 8 常用写法 |
| PostgreSQL | IS NOT DISTINCT FROM | 标准语法,可读性好 |
| Spark SQL | a <=> b | 两种写法都支持 |
| Hive | coalesce 兜底 | 旧版本无专用运算符 |
把空值视为有效值时,先落默认值(如「未知」)再比较能简化逻辑,但占位符约定必须统一写进维度规范,否则各表占位不一致反而更难排查。
拼接指纹前先统一空值表示:空串与 NULL 的拼接结果不同,会让同一业务状态在不同批次算出不同指纹,产生大量伪变更。
入维前统一空值占位比事后逐列比较更省事:把空串、NULL 以及字符串形式的空值都规整成同一占位符,可以一次性消除大部分伪变更。