首页/数据/数仓建模速查/缓慢变化维 SCD 总览
维度与事实设计149 条

缓慢变化维 SCD 总览

维度属性随时间变化的处理方式按 Kimball 划分为 0/1/2/3/4/6 等类型;选型取决于是否需要历史、变更频率与查询模式,同一维度不同属性可混用不同策略。

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

缓慢变化维总览

维度属性会随时间变化,处理方式按 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 处理会让维表体积快速膨胀,而其中大量属性并没有历史分析价值。
把本应保留历史的属性改成覆盖式更新后,历史无法回溯,而且问题通常在一次审计或同比分析时才发现,届时数据已经无法恢复。

易错点

  • 所有属性一律按类型 2 处理:维表行数爆炸,join 成本与存储同步上升
  • 需要审计的属性用了覆盖式更新:历史永久丢失,且不可逆
  • 同一属性在不同表里用了不同 SCD 类型:跨表对比时口径打架
  • 选型时不问业务是否需要历史:为省事全用类型 1,等要复盘时只能重建

数据来源与参考

  • 维度建模工具箱:缓慢变化维
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌