首页/数据/数仓建模速查/SCD 类型 2:新增版本行
维度与事实设计149 条

SCD 类型 2:新增版本行

每次属性变更插入新行并维护起止日期与当前标志,是保留历史的主流方案;依赖代理键、版本区间与幂等合并逻辑。

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

SCD 类型 2 是什么

SCD 类型 2 的做法是属性变更时插入新版本行,并用起止日期与当前标志标注有效期,旧行被关闭而不是删除。它是保留历史的主流方案,代价是维表行数随变更次数增长,查询必须带版本区间条件。实现上依赖三个要素:代理键保证每个版本有唯一主键、版本区间描述生效时段、hashdiff 用于快速判断属性是否真的变化,避免无变更也插新行。

基本信息

行数每个版本一行,历史全保留
关键列start_date / end_date / is_current / hashdiff
主键代理键(版本唯一),业务键可重复出现
合并逻辑按自然键与 hashdiff 比较,变化才插新行
区间约定闭开区间 [start, end),当前行 end 取远期值

典型使用场景

  • 用户等级与会员状态:按当时等级统计消费,版本行是唯一可行方案
  • 商品价格与类目:需要回放历史归属,支撑同比与调价影响分析
  • 客户归属销售:人员调整后历史订单仍按当时归属归属计算提成
  • 账户状态变更:冻结与解冻轨迹需要完整留存,满足审计要求
  • 组织架构调整:人员部门变化按版本记录,历史报表口径可复现

两段式合并写法

两段式合并写法
-- 1) 关闭发生变更的旧版本(带 is_current 与 hashdiff 双条件)
UPDATE dim_user
   SET end_date = '2026-10-12', is_current = 0
 WHERE user_id = :id
   AND is_current = 1
   AND hashdiff <> :new_hash;    -- 属性未变则不产生新版本

-- 2) 插入新版本行,start_date 为变更生效时间
INSERT INTO dim_user
  (user_id, name, tier, start_date, end_date, is_current, hashdiff)
VALUES (:id, :name, :tier, '2026-10-12', '9999-12-31', 1, :new_hash);
-- 未变更的维度成员保持原行不动,不做全表重建

关键列与作用

列作用缺失后果
代理键版本行唯一标识,事实表引用同业务键多行冲突,join 放大
hashdiff判断属性是否真变化无变更也插版本,维表迅速膨胀
is_current快速取当前版本每次查当前值都要比日期
end_date界定版本有效期区间无法判断,历史口径混乱
版本行要与事实加载顺序对齐。事实先落地而维度版本尚未生效时,事实会关联到旧版本或默认键,造成一段时间的口径偏差。
hashdiff 覆盖的属性集合要固定并写进文档:变更属性集合等于变更口径,如果有人在计算哈希时多加了字段,历史版本判断会整体失灵。

易错点

  • 只关闭旧版本不插入新版本:该成员在当前版本集合里消失,事实关联直接断链
  • 起止日期使用闭区间:相邻版本在边界日期重叠,同一天命中两条版本
  • hashdiff 计算含入库时间等易变字段:每次加载都判定为变更,维表每天全量膨胀
  • 事实加载不带版本区间条件:只能关联到当前版本,历史归因全部错误

数据来源与参考

  • 维度建模工具箱:缓慢变化维
  • 数据仓库工具箱
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌