商品价格每天都在变,你怎么设计这张维度表?
先说结论
缓慢变化维有三种处理方式:
Type 1(直接覆盖): 新值直接覆盖旧值,不保留历史。最简单但丢失了变更历史。
Type 2(新增行): 每次变更新增一行,用start_date/end_date标记有效期。生产环境最常用。
Type 3(新增列): 加一列存旧值。只能跟踪一次变更,很少用。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
缓慢变化维(SCD)是数仓建模的核心问题,也是区分“会写SQL”和“懂建模”的分水岭。很多人知道Type 1/2/3的定义,但问到“具体怎么实现”就卡住了。今天用一个真实的商品价格场景来讲透。
业务场景:短剧平台的商品价格表
我们公司的短剧商品有币种、定价策略的变化。比如同一个短剧在不同地区价格不同,而且运营经常调价。我用的是Type 2:
CREATE TABLE dim_product (
product_id STRING,
product_name STRING,
price DECIMAL(10,2),
currency STRING,
region STRING,
start_date STRING,
end_date STRING, -- '9999-12-31'表示当前有效
is_current BOOLEAN -- 快速过滤当前有效版本
)
STORED AS PARQUET;
每当价格变更时:
1. 把旧记录的end_date从'9999-12-31'更新为昨天的日期,is_current设为false
2. 插入一条新记录,start_date为今天,end_date为'9999-12-31',is_current为true
实现SQL(用MERGE INTO)
-- 每天的增量更新逻辑
-- Step 1: 关闭已变更的旧记录
UPDATE dim_product
SET end_date = '2025-03-14', is_current = false
WHERE product_id IN (SELECT product_id FROM stg_product_update)
AND is_current = true;
-- Step 2: 插入新版本
INSERT INTO dim_product
SELECT product_id, product_name, new_price, currency, region,
'2025-03-15' AS start_date,
'9999-12-31' AS end_date,
true AS is_current
FROM stg_product_update;
如果用Delta Lake或Iceberg,可以直接用MERGE INTO一条语句搞定,代码量少很多。
面试高频追问:查询时怎么关联事实表?
用事实表的业务日期和维度表的有效期区间做关联:
SELECT f.order_id, f.amount, d.price, d.currency
FROM fact_order f
JOIN dim_product d
ON f.product_id = d.product_id
AND f.order_date >= d.start_date
AND f.order_date < d.end_date;
这样每笔订单都能关联到下单时刻的正确价格,而不是当前价格。这就是Type 2的核心价值。
总结
记住一句话:Type 2是生产环境最常用的SCD方案,核心是start_date/end_date + is_current。面试的时候能用一个真实场景说清楚“为什么选Type 2”和“具体怎么实现”,就很稳了。
下一篇聊一个每个ETL开发都会遇到的问题:Hive小文件太多怎么办?查询慢得要命。
你们的维度表用的是哪种SCD方案?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长