缓慢变化维(SCD)
总结
| 记忆点 | 内容 |
|---|---|
| SCD核心问题 | 维度变了,历史要不要保留? |
| Type 1 | 直接覆盖,丢历史,最简单 |
| Type 2 | 拉链表,保留完整历史,最常用 |
| Type 3 | 新增列,只存前一次值,极少用 |
| 拉链表三字段 | start_date / end_date / is_current |
| 查询关键 | 事实表日期落在维度的有效期范围内 |
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
基础 | 大数据面试 · 数仓建模系列
| 面试真题 用户换了手机号、商品改了价格,维度表怎么处理?请介绍SCD的几种类型,你们用的是哪种? |
|---|
什么是缓慢变化维?
维度表的数据不是一成不变的。用户会改地址,商品会调价格,店铺会换名字——这些变化不频繁但确实会发生,就叫缓慢变化维(Slowly Changing Dimension)。
核心问题只有一个:变化了,历史数据要不要保留?不同的回答对应不同的SCD类型。
| 类型 | 策略 | 一句话总结 |
|---|---|---|
| Type 1 | 直接覆盖 | 不要历史,只看最新 |
| Type 2 | 新增行(拉链表) | 完整保留历史,最常用 |
| Type 3 | 新增列 | 只保留上一次的值 |
Type 1:直接覆盖(最简单)
用户换了手机号,直接UPDATE旧记录。简单粗暴,表永远只有最新状态。优点是简单,缺点是历史丢失,无法回溯「用户之前的手机号是什么」。适用于修正数据错误、不关心历史的场景。比如用户的昵称写错了,直接改就行,没必要保留错误的历史。
Type 2:拉链表(面试必考)
这是面试考得最多的类型,也是生产中最常用的方案。核心思路是:不改旧数据,新增一行,用start_date、end_date、is_current三个字段标记有效期。
举例:用户张三从手机号138xxxx改成139xxxx。变更前只有一条记录,变更后变成两条:旧记录的end_date改成变更日期前一天,is_current改成0;新增一行记录当前状态,end_date设为9999-12-31,is_current为1。
这样做的好处是,你可以查到任意历史时间点的用户状态。比如分析订单时,你想知道用户下单时的地址是什么(而不是现在的地址),就需要用拉链表做范围JOIN。
拉链表的三种查询姿势
| -- 1. 查当前有效数据 SELECT * FROM dim_user WHERE is_current = 1; -- 2. 查某个历史时间点的快照(2024年6月1日的状态) SELECT * FROM dim_user WHERE start_date <= '2024-06-01' AND end_date > '2024-06-01'; -- 3. 事实表关联历史维度(订单发生时用户的状态) SELECT f.order_id, f.amount, d.phone, d.city FROM fact_order f JOIN dim_user d ON f.user_id = d.user_id AND f.order_date >= d.start_date AND f.order_date < d.end_date; |
|---|
| Wood的实战经验 end_date的处理要统一,这是很容易踩的坑。有的团队用闭区间(end_date = '2025-01-14'),有的用开区间(end_date = '2025-01-15')。 我们团队统一用左闭右开(start_date <= 且 end_date >),和事实表的日期分区逻辑保持一致,能避免很多边界Bug。这个看似小的约定,实际上帮我们省了很多排查时间。 常见追问:拉链表数据量会不会膨胀?答案是:只有发生变化的行才新增记录,不变的行不增长。高频变化的维度(如实时价格)不适合拉链表,考虑快照表。 |
|---|
Type 3:新增列(用得少)
只关心「当前值」和「上一次的值」,加两列搞定(如current_city和previous_city)。优点简单不增加行数,缺点只保留一次历史,变化3次以上就丢数据了。极少使用。
| 面试加分提示 面试回答SCD时,不要只背Type 1/2/3的定义,要能说出你的实战选择和原因: 「我们主要用Type 2拉链表。比如用户维度表,用户可能改手机号、改收货地址,我们需要在分析订单时关联到下单时的用户状态,所以必须保留历史。拉链表用start_date/end_date做有效期标记,事实表通过订单日期和有效期做范围JOIN。」 追问防线:面试官可能追问「拉链表数据量会不会膨胀」——只有发生变化的行才新增记录;高频变化的维度不适合拉链表,考虑快照表;定期归档过老的历史数据,控制表大小。 |
|---|