Type 2 怎么做版本化
Type 2 在属性发生变化时,先把当前版本关链(写入结束日期、把当前标志置为否),再插入一条新版本行(新的代理键、新的生效起始日期)。事实表写入时取事件发生时刻生效的那个代理键,因此历史指标可以按当时口径复现。这是数仓保留历史最常用的方式,代价是同一实体在维度表中会有多行。
基本信息
| 字段 | 代理键、业务键、属性、start_date、end_date、is_current |
|---|---|
| 变化处理 | 旧行关链,同时新增一行 |
| 事实关联 | 写入时取事件时间下生效的代理键 |
| 能力 | 支持按历史属性回溯分析 |
典型使用场景
- 会员等级变化:保留每次升级记录,支持按当时等级统计
- 组织归属调整:历史业绩按当时所属组织归集
- 商品类目调整:类目变化后历史销量仍归到旧类目
- 补历史数据:按事件时间匹配当时生效的版本
- 维度治理:只对需要回溯的属性启用版本化
两段式维护
两段式维护
-- 1) 关闭发生变化的当前版本
UPDATE dim_user SET end_date = DATE '2026-10-11', is_current = false
WHERE user_id IN (SELECT user_id FROM stg_user_changed) AND is_current = true;
-- 2) 插入新版本行
INSERT INTO dim_user (user_sk, user_id, user_name, city, level, start_date, end_date, is_current)
SELECT seq_user_sk.NEXTVAL, user_id, user_name, city, level,
DATE '2026-10-11', DATE '9999-12-31', true
FROM stg_user_changed;版本行字段读法
| 字段 | 含义 | 注意点 |
|---|---|---|
| user_sk | 代理键,每个版本不同 | 事实表关联的就是它 |
| user_id | 业务键,同一实体不变 | 用于找出同一实体的所有版本 |
| start_date | 本版本生效日期 | 取区间时按左闭处理 |
| end_date | 本版本失效日期 | 当前版本写极限日期 |
| is_current | 是否当前版本 | 辅助标记,不能替代区间条件 |
事实表写入时要按事件时间匹配生效中的维度版本,而不是简单取 is_current = true,否则补历史数据会取到最新版本,历史口径被污染。
end_date 用极限日期和用 NULL 混用,会让关联条件写法不一致:同一套 SQL 在不同表上结果不同,排查时极难发现。
版本化带来的成本
Type 2 的代价要说清楚:同一实体在维度表里会有多行,维度表行数随变更次数线性增长,关联时如果只按业务键 join 会产生笛卡尔放大。所以事实表必须存代理键、关联时按代理键等值 join;只有需要按时间区间回溯时才用业务键加区间条件。另一个常见问题是变更判定:只比较业务属性列,把更新时间之类的技术字段排除在外,否则每次同步都会被判定为「发生变化」,维度版本会无意义地膨胀。