缓慢变化维总览
维度属性会随时间变化,处理方式按 Kimball 的分法归为若干类型:类型 0 保留原值忽略变更,类型 1 直接覆盖不保留历史,类型 2 新增版本行保留完整历史,类型 3 增加前值列,类型 4 把历史单独放历史表,类型 6 是 1+2+3 的混合。选型要看三件事:该属性是否需要历史、变更频率有多高、查询模式是取当前值还是看历史。同一维度的不同属性可以各自采用不同类型,不必整表统一。
基本信息
| 类型 0 | 保留原值,忽略变更,常见于创建时点属性 |
|---|---|
| 类型 1 | 覆盖旧值,只保留最新,实现最简单 |
| 类型 2 | 新增版本行,保留全部历史,主流方案 |
| 类型 3 | 增加前值列,只保留上一版本 |
| 类型 6 | 当前值列 + 版本行 + 前值列混合,兼顾两种查法 |
典型使用场景
- 用户等级变化:需要按当时等级统计消费,用类型 2 保留版本
- 手机号与邮箱修正:属于数据纠错而非业务变更,通常用类型 1 覆盖
- 商品类目调整:需追溯历史归属用于同比分析,用类型 2
- 创建渠道:注册时确定后不再变化,用类型 0
- 频繁变动的等级区间:考虑拆出迷你维度,而不是让主维無限膨胀
按类型取数
按类型取数
-- 类型 2 维表:查当前有效版本
SELECT * FROM dim_user WHERE is_current = 1;
-- 类型 2 维表:按业务时间取当时版本(不是当前版本)
SELECT d.user_id, d.tier
FROM fact_order f
JOIN dim_user d ON f.user_id = d.user_id
AND f.create_time >= d.start_date
AND f.create_time < d.end_date; -- 闭开区间,避免边界重叠
-- 类型 1 覆盖式:历史已不可回溯,只能查现值
-- SELECT user_id, phone FROM dim_user; -- phone 无版本历史类型选择对照
| 属性特点 | 推荐类型 | 理由 |
|---|---|---|
| 需要历史追溯 | 类型 2 | 每个版本一行,口径可回放 |
| 仅纠正错误值 | 类型 1 | 覆盖即可,避免无意义版本行 |
| 仅需当前与前值 | 类型 3 | 列少、查询简单 |
| 同时要当前值与历史 | 类型 6 | 当前列供快速查询,版本行供追溯 |
| 完全不变的属性 | 类型 0 | 无需任何额外机制 |
同一维表可以混用多种类型:创建时间用类型 0,手机号用类型 1,等级用类型 2,这是正常做法。整表一刀切按类型 2 处理会让维表体积快速膨胀,而其中大量属性并没有历史分析价值。
把本应保留历史的属性改成覆盖式更新后,历史无法回溯,而且问题通常在一次审计或同比分析时才发现,届时数据已经无法恢复。