首页/数据/数仓建模速查/SCD 类型 4:主表与历史表分离
维度与事实设计149 条

SCD 类型 4:主表与历史表分离

主表保存最新值供高频查询,历史变更写入独立历史表;适合大部分查询只用最新、少数场景才看历史的情况。

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

类型 4 是什么

类型 4 把「最新值」与「历史变更」拆到两张表:主表只保留每个实体的当前属性(覆盖式更新,一个实体一行),另建历史表追加记录每次变更。主表体积小、查询快,适合绝大多数只看最新值的场景;需要追溯时再查历史表。与拉链表(类型 2)的关键差别是不把版本行堆在同一张表里,因此主表查询不需要 is_current 过滤。

基本信息

结构主表(最新)+ 历史表(变更流水)
主表更新覆盖式,一个实体一行
历史表更新只追加,含变更前后值与变更时间
查询方式最新值查主表,追溯按需关联历史表
与类型 2 差别版本行分表存放,主表无需当前标志

典型使用场景

  • 主数据同步:客户、商品等维度日常只需最新属性,主表即可满足
  • 审计与追溯:合规或客服场景要看某个属性历史被改过几次
  • 高频查询优化:主表无版本行,join 不需要过滤当前标志,性能稳定
  • 变更分析:历史表天然支持「本月有多少商品调整过价格」这类统计
  • 渐进式改造:已有主表先补一张历史表,避免一次性重构成拉链
  • 权限与口径并行:业务查主表,审计查历史表,互不影响

两表结构与查询

两表结构与查询
-- 主表:一个实体一行,覆盖式更新
dim_user       -- user_sk, user_id, name, city, tier

-- 历史表:每次变更追加一行
dim_user_hist  -- user_id, attr_name, old_value, new_value, changed_at

-- 日常查最新属性:只查主表,无需过滤当前标志
SELECT user_id, name, tier FROM dim_user WHERE user_id = 'U10086';

-- 追溯某个属性的变更轨迹
SELECT changed_at, old_value, new_value
FROM   dim_user_hist
WHERE  user_id = 'U10086' AND attr_name = 'tier'
ORDER  BY changed_at;

类型 4 与类型 2 对比

对比项类型 4类型 2 拉链
存放方式最新与历史分两张表版本行在同一张表
最新值查询直接查主表需过滤当前标志或取最大版本
历史查询查历史表或按需关联同表按生效区间过滤
表体积主表小、历史表持续增长单表持续增长,索引压力大
更新复杂度两表需同步更新,一致性要保证需先关闭旧版本再写入新版本
回看任意时点需按变更流水重演直接按时间区间筛选
类型 4 与迷你维度思路相近,区别在于迷你维度面向高频离散属性的组合枚举,用更少的行承载组合变化;类型 4 关注的是把历史与最新拆开存放。两表更新必须在同一事务或同一调度步骤完成,否则会出现主表已改、历史表没记的断链。

易错点

  • 双表更新不同步:主表已是最新值,历史表没记录,追溯时发现变更凭空丢失
  • 使用方不清楚该查哪张表:有人查主表当历史用,口径混乱且无从解释
  • 历史表只记新值不记旧值:只能看到变过,无法还原变更前的状态
  • 历史表按整行快照记录:行数与属性数同增,体积膨胀远超预期
  • 主表覆盖更新无并发保护:两个任务同时写,后到的把先到的静默覆盖

数据来源与参考

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