首页/数据/数据工程与 ETL 速查/空值与默认值陷阱

空值与默认值陷阱

空值与默认值是两件事:默认值会掩盖缺失(空串、0、1970 时间戳),把“没有数据”伪装成“有数据”;校验要同时查 NULL 与可疑默认值,并统一指标计算中 NULL 的处理方式(COUNT 忽略 NULL、AVG 忽略 NULL)。

空值与默认值陷阱

空值与默认值看起来都是“没填”,含义却不同:NULL 表示未知,默认值(空串、0、1970 时间戳)表示填了个占位符。后者会把缺失伪装成有数据,让 not_null 一类的校验全部通过。校验要同时查 NULL 与可疑默认值,并把聚合时的 NULL 处理口径写进文档,否则跨团队对账时各算各的。

基本信息

NULL 语义未知值,参与 COUNT/AVG 时被忽略
常见默认值空串、0、-1、1970-01-01 00:00:00
检查方式分别统计 NULL 条数与默认值占比
聚合口径COUNT/AVG 忽略 NULL;SUM 返回 NULL 表示全为 NULL
影响面指标口径、对账结果、下游判空逻辑

典型使用场景

  • 上游用空串代替 NULL 时,检查 not_null 规则是否被绕过
  • 时间字段出现 1970 时间戳,判断是否为默认占位值
  • 金额为 0 的记录激增时,区分真实零值与默认填充
  • 跨团队对账前统一 NULL 口径,避免 AVG 结果对不上
  • 迁移或同步后抽查默认值占比,验证类型映射是否正确
  • 下游关联查询结果变少时,排查是否因 NULL 参与等值比较而不匹配

同时检查 NULL 与默认值

同时检查 NULL 与默认值
SELECT
  SUM(CASE WHEN amount IS NULL THEN 1 ELSE 0 END) AS null_cnt,   -- 真缺失
  SUM(CASE WHEN amount = 0 THEN 1 ELSE 0 END)     AS zero_cnt,   -- 可疑默认值
  SUM(CASE WHEN created_at = '1970-01-01 00:00:00' THEN 1 ELSE 0 END) AS epoch_cnt
FROM dwd_orders
WHERE dt = '2026-10-11';

占比与口径差异

占比与口径差异
-- 占比:只看绝对条数会被大表体量稀释
SELECT
  COUNT(*) AS total,
  SUM(CASE WHEN amount IS NULL THEN 1 ELSE 0 END) / COUNT(*) AS null_ratio
FROM dwd_orders
WHERE dt = '2026-10-11';

-- 口径差异:AVG 忽略 NULL 与把 NULL 当 0 参与计算的结果并不相同
SELECT AVG(amount)           AS avg_ignore_null,
       SUM(amount) / COUNT(*) AS avg_null_as_zero
FROM dwd_orders
WHERE dt = '2026-10-11';

NULL 与默认值对照

表现真实含义处置
NULL未知或未采集按口径决定是否参与聚合
空串上游用占位符代替 NULL写规则识别,必要时归一为 NULL
0 或 -1默认填充或真实零值结合业务分布判断,勿一律当缺失
1970-01-01时间字段默认占位回溯上游采集逻辑,标记为无效
指标的 NULL 口径要写进文档:AVG 忽略 NULL 与把 NULL 当 0 的结果差异很大,跨团队口径不同会导致对账争议。
不同引擎的默认行为不同:MySQL 在非严格模式下会把非法值静默转成默认值,仅告警不报错,建表与导入时要留意 sql_mode 设置。
判空写法要统一:NULL 只能用 IS NULL 判断,写成 = NULL 恒不成立;空串与 NULL 混用的字段,先归一再统计,否则同一份数据会得出两套缺失率。

易错点

  • 上游用空串代替 NULL,not_null 校验全通过,实际全是缺失
  • 把 NULL 当 0 参与平均,指标被系统性低估
  • 只在样本上验证默认值占比,抽样窗口恰好避开了问题数据的集中时段
  • 不同引擎的 NULL 排序与去重规则不一致,跨库比对时把差异当成数据错误

数据来源与参考

  • SQL 标准实践(NULL 三值逻辑)
  • 行业数据质量实践
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌