首页/数据/数仓建模速查/SCD 类型 6:混合方案
维度与事实设计149 条

SCD 类型 6:混合方案

类型6 组合类型1、2、3:保留历史版本行,同时维护当前值列与前值列;同一属性可分列满足「历史当时」与「当前回溯」两种口径。

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

类型 6 是什么

类型 6 是类型 1、2、3 的组合:既保留类型 2 的历史版本行,又在同一行上维护「当前值列」和「前值列」,并用类型 1 的方式把当前值覆盖到该实体的所有历史行。于是同一属性有两套读法:按历史行读是「当时口径」,按当前值列读是「用现在的属性回溯历史行为」。它解决同一份事实需要两种归因口径的问题,代价是维护与文档成本明显上升。

基本信息

结构历史版本行 + 当前值列 + 前值列
更新方式新增版本行时同步把当前值覆盖到全历史行
两种口径历史行属性为当时口径;当前值列为现行口径
适用动机同一事实需要按当时与按现在两种方式归因
成本维护、存储与文档成本显著高于类型 2

典型使用场景

  • 按当前属性看历史:把老客户按现在的等级重新分组,分析历史消费行为
  • 按当时属性看当时:同一份事实用历史行取当时等级,做当时口径统计
  • 迁移分析:等级调整前后对比,前值列可直接读取变化
  • 口径切换过渡期:新旧口径并存期间同时提供两套读法,报表不中断
  • 分层策略复盘:current 列做现行分组,hist 列核对历史归属是否正确
  • 监管对比:既要按报送时点的口径,也要按最新归类的口径出数

当前值列回写

当前值列回写
-- 第 1 步:新增版本行(类型 2 的处理)
INSERT INTO dim_user (user_sk, user_id, tier, tier_prev, tier_cur,
                      start_dt, end_dt, is_current)
SELECT next_sk(), user_id, 'GOLD', tier, 'GOLD',
       current_date, '9999-12-31', 1
FROM   dim_user WHERE user_id = 'U10086' AND is_current = 1;

-- 第 2 步:关闭旧行
UPDATE dim_user SET end_dt = current_date - 1, is_current = 0
WHERE  user_id = 'U10086' AND is_current = 1 AND tier <> 'GOLD';

-- 第 3 步:类型 1 回写,把当前值覆盖到该用户的全历史行
UPDATE dim_user SET tier_cur = 'GOLD' WHERE user_id = 'U10086';

三列读法

列含义适用口径
tier(行值)该版本生效期间的属性值当时口径:事实按发生时的属性归因
tier_cur当前最新值,覆盖到所有历史行现行口径:用现在的属性回溯历史
tier_prev上一个版本的值变化对比:直接看从哪一档迁到哪一档
start_dt / end_dt版本生效区间时点查询与有效期校验
is_current是否当前版本行定位最新行,唯一性必须校验
类型 6 最大的风险不是实现,而是两套口径混用:同一个字段名在不同报表里读法不同,数字必然矛盾。字段名要体现口径(tier 与 tier_cur),指标注释与报表标题都要标明采用哪一种,否则维护几年后没人能解释差异。

易错点

  • 复杂度过高且缺文档:维护与传承成本大,换一批人后没人敢改
  • 两套口径混用且字段名不区分:报表间口径相互矛盾,业务方认为数据不可信
  • 类型 1 回写遗漏历史行:只改了当前行,老版本行的当前值列仍是旧值
  • 回写与新增版本行不在同一批次:中途失败后当前值列与版本行状态不一致
  • 当前值列参与版本比较:哈希或变更检测把它当跟踪属性,每批全量变更

数据来源与参考

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