首页/数据/数仓建模速查/hashdiff 变更检测
维度与事实设计149 条

hashdiff 变更检测

把需跟踪的属性按固定顺序拼接后取哈希作为 hashdiff 列,比较新旧哈希即可判断是否变更,免去逐列比较。

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

hashdiff 是什么

hashdiff(哈希差异列)把需要跟踪变更的属性按固定顺序拼成一个字符串再取哈希,作为一列存在维表上。更新时只比较新旧 hashdiff 是否相等,就能判断成员属性有没有变,省掉逐列书写比较条件。SCD2 拉链更新、增量同步与数据质量比对都靠它做变更检测。它只回答「变没变」,不回答「哪一列变了」。

基本信息

拼接方式跟踪属性按固定顺序 concat,空值先占位
算法md5(32位)、sha1(40位)、sha256(64位) 均可,全链路统一
存储varchar 或 char,长度与算法输出等长
比较语义相等=未变更,不等=有变更,NULL 视为未计算
生成时机抽取层或 staging 层统一生成,维表只接收结果

典型使用场景

  • SCD2 拉链更新:只对哈希变化的成员关闭旧行、开新版本行
  • 增量入仓:源表全量抽取后按哈希挑出真正变化的行
  • 数据质量比对:上下游两份数据按哈希快速定位差异成员
  • 多列去重:宽表按业务键去重时的变更判断
  • 变更审计:版本行留一列 hashdiff,便于回溯每次记录的变化
  • 口径回归:口径调整前后各算一次哈希,量化受影响范围

hashdiff 生成

hashdiff 生成
-- 拼接顺序与占位符全局统一;改动等于全量误判
SELECT id,
       md5(concat_ws('|',
             coalesce(name,  '#'),
             coalesce(phone, '#'),
             coalesce(tier,  '#'))) AS hashdiff
FROM stg_user;   -- 空值统一占位,避免 NULL 传染整个哈希

常见写法对照

写法说明风险
concat_ws + coalesce分隔符固定,空值占位值里出现分隔符会串位,需确认值域
concat 直接拼不处理 NULL任一列为 NULL 则整行结果为 NULL,变更永远测不到
先 to_date/to_char 再拼消除格式差异日期格式不统一仍会误判
按表结构列序全拼属性直接按存储顺序拼加字段后顺序漂移,一夜全量变更

增量落地

增量落地
-- 1) 只取哈希变化或新增的成员
SELECT s.id, s.hashdiff
FROM   stg_user s
LEFT JOIN dim_user d
       ON s.id = d.user_id_nk
      AND d.is_current = 1
WHERE  d.user_sk IS NULL
   OR  s.hashdiff <> d.hashdiff;

-- 2) 跑完看变更率是否合理,异常升高说明拼接口径变了
哈希只做「变没变」的判断,若要排查具体差异,仍需保留一份原值快照或 staging 数据。md5 在维表规模下碰撞概率可忽略;审计要求强一致时改用 sha256,并保留拼接明文以便复算。

易错点

  • 拼接顺序或分隔符上下游不一致:同一份数据两边算出不同哈希,全量误判为变更,拉链表一夜翻倍
  • 空值未占位:concat 遇 NULL 整行结果为 NULL,比较恒不成立,变更检测静默失效
  • 把更新时间、ETL 批号这类每批必变的列拼进哈希:每批全量开新版本,拉链失去意义
  • 哈希列长度没跟算法对齐(md5 写 varchar(64) 或反过来截断):比较与存储双双出错
  • 只看哈希不等就插新行,跳过了结束旧行的同一批次约束:同一自然键会残留两条当前行

数据来源与参考

  • 拉链表实现实践
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌