首页/数据/数仓建模速查/空值与默认值:区分未知与不适用
分层与规范64 条

空值与默认值:区分未知与不适用

空值语义要区分:未知(源缺失)用 null、不适用(逻辑上不存在)用特殊成员或约定值;禁止用 0、-1、1970-01-01 混当「空」。

数仓建模速查数据更新于 2026-10-13

空值不是一种东西

空值至少有三种语义:源端缺失导致未知、业务逻辑上不适用、以及度量本身为零。三者混用会让统计结果失真,也会让下游判断出错。规范做法是未知保留 null,不适用用明确的特殊成员或约定值,真正的零就存零,不要用 0、-1、1970-01-01 这些魔法值去兼任「空」。

基本信息

未知(源缺失)保留 null,不做默认值填充
不适用用特殊成员或约定字符串(如 NONE)
度量为零存真实的 0,与缺失区分开
禁止用 0 / -1 / 1970-01-01 混当空
统计口径count 与 sum 对 null 的处理差异要写明

典型使用场景

  • 优惠券字段:未使用券是「不适用」,用 NONE 明确表达,而不是 null
  • 发货时间:未发货属于「未发生」,保留 null,等发货再回填
  • 退款金额:没有退款就是真实的 0,不能写成 null 让平均值失真
  • 用户年龄:源端没采集是「未知」,保留 null 并允许下游按需过滤
  • 日均值计算:先明确分母是总记录数还是非空记录数,两种口径结果不同

空值处理示例

空值处理示例
-- 区分「不适用」与「未发生」
SELECT order_id,
       COALESCE(coupon_id, 'NONE') AS coupon_id,  -- 不适用:明确无券
       deliver_time,                              -- 未发货:保留 null
       sum(refund_amt) AS refund_amt              -- 未退款:真实 0
FROM dwd_trade_order_di
WHERE dt = '2026-10-12'
GROUP BY order_id, coupon_id, deliver_time;

空值语义对照

场景推荐取值说明
源端未采集null保留未知,禁止填 0
逻辑不存在(无券)特殊成员 NONE可参与聚合不产生歧义
度量无发生(退款)0是真实值,参与求和不失真
时间未发生(发货)null避免参与时长计算
字符串未填写null 或空串择一全链路统一,不混用
count(1) 统计行数,count(col) 跳过该列为 null 的行;指标口径里写「客户数」时必须说明用的是哪一种,否则同一个数两个人都能算出不同结果。同理,sum 会忽略 null,若先用 0 填充再求和,平均值就会被拉低。

落地检查项

  • 列出全部度量字段,逐个确认「缺失」与「为零」是否需要用不同取值表达
  • 检查历史脚本里有没有 COALESCE(x, 0) 这类无条件填充,区分哪些是有意为之
  • 约定特殊成员的取值范围(如 NONE、UNKNOWN),并保证不与管理口径冲突
  • 在字段注释里写明空值语义,让下游不必靠猜
用 1970-01-01 表示未发生的时间,会让时长统计出现五十年量级的异常值;做环比、留存、时效分析时这类数据必须先行剔除。

易错点

  • 用 0 表示未知金额,平均值与人均指标被系统性拉低
  • 同一字段在不同链路里既用 null 又用 -1 表示缺失,下游过滤条件漏写一边就多算一批数据
  • 把 null 参与比较运算,过滤条件写成 col <> 0 时 null 行被静默丢弃
  • 用 1970-01-01 代替空时间,时长与时效指标出现无法解释的极值

数据来源与参考

  • Kimball《数据仓库工具箱(第3版)》
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌