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 覆盖的属性集合要固定并写进文档:变更属性集合等于变更口径,如果有人在计算哈希时多加了字段,历史版本判断会整体失灵。