3. 退化维度陷阱:将业务主键错误处理为事实,丢失维度属性
这个陷阱,我见过太多次了。
很多刚接触维度建模的同事,看到订单号、交易流水号、发票号这类字段,第一反应就是:「这不就是个ID嘛,扔到事实表里当度量好了。」
嗯,表面上看确实是个ID。但你仔细想想——业务主键背后,往往藏着丰富的维度属性。一旦你把它当事实处理,这些属性就全丢了。
3.1 什么是退化维度?
先明确概念。退化维度(Degenerate Dimension),指的是那些没有独立维度表、但包含维度属性的维度。它通常以业务主键的形式出现在事实表中。
举个例子:
- 订单号本身不是度量,它不能求和、不能平均
- 但订单号可以关联出:下单时间、客户ID、门店ID、支付方式
- 这些属性如果被丢弃,后续分析就断了
说白了,退化维度是「有维度属性,但没建维度表」的特殊维度。它应该留在事实表中,但要以维度字段的身份存在,而不是度量。
核心判断标准:
- 能参与聚合运算(SUM、AVG)→ 事实
- 不能参与聚合,但能分组、筛选、关联 → 维度
- 业务主键几乎永远是维度,不是事实
3.2 错误处理的典型场景
我在项目中遇到过这样一个案例:
某电商团队做销售分析,把「订单号」直接放进了事实表的度量区。结果呢?
- 订单号被当成数值型度量,系统自动做了SUM
- SUM(订单号) 毫无意义,还占用了大量存储
- 更糟的是,订单相关的门店、渠道、促销活动信息全丢了
后来他们想分析「不同门店的订单分布」,发现根本做不了——因为门店信息只存在于订单维度,而订单维度已经被「优化」掉了。
⚠️ 常见错误模式:
- 把业务主键放在事实表的度量列(numeric类型)
- 为了「节省空间」删除订单维度表,只保留订单号
- 把订单号当作退化维度,但没保留任何维度属性
- 在ETL中直接丢弃业务主键,认为「反正用不到」
3.3 正确做法:保留退化维度,但不丢失属性
正确的处理方式其实不复杂:
- 业务主键留在事实表,但作为维度字段(varchar类型)
- 提取关键维度属性,建立退化维度表或直接保留在事实表
- 区分核心属性与冗余属性,避免过度建模
我个人的习惯是:
- 如果业务主键关联的属性少于3个,直接放在事实表中
- 如果属性超过3个,建一个独立的退化维度表
- 永远保留业务主键本身,它是数据血缘的锚点
💡 实用技巧:
订单号、交易流水号这类字段,建议保留为varchar类型。即使你暂时用不到它的属性,未来做数据回溯、问题排查时,它都是救命稻草。
3.4 代码示例:错误 vs 正确
先看错误做法:
-- ❌ 错误:订单号被当作度量
CREATE TABLE fact_sales (
sale_id INT PRIMARY KEY,
order_id BIGINT, -- 当作数值度量
product_id INT,
amount DECIMAL(10,2),
quantity INT
);
-- 查询时:SUM(order_id) 毫无意义
SELECT SUM(order_id) FROM fact_sales;
再看正确做法:
-- ✅ 正确:订单号作为退化维度字段
CREATE TABLE fact_sales (
sale_id INT PRIMARY KEY,
order_id VARCHAR(50), -- 维度字段,varchar类型
product_id INT,
amount DECIMAL(10,2),
quantity INT,
order_date DATE, -- 退化维度属性
store_id INT, -- 退化维度属性
channel VARCHAR(20) -- 退化维度属性
);
-- 查询时:按订单维度分组
SELECT
store_id,
channel,
COUNT(DISTINCT order_id) AS order_count,
SUM(amount) AS total_amount
FROM fact_sales
GROUP BY store_id, channel;
3.5 什么时候可以「退化」?
不是所有业务主键都需要建维度表。我总结了几条经验:
| 场景 | 建议处理方式 | 原因 |
|---|---|---|
| 订单号(属性多) | 建退化维度表 | 关联客户、门店、促销等5+属性 |
| 交易流水号(属性少) | 直接保留在事实表 | 通常只有时间、渠道2-3个属性 |
| 发票号(属性中等) | 视情况而定 | 如果只关联开票日期和金额,放事实表即可 |
| 物流单号(属性多) | 必须建维度表 | 涉及物流商、仓库、配送状态等复杂属性 |
3.6 避坑指南
我曾经踩过的坑:
- 为了「性能优化」把订单号从事实表删掉,结果数据对账花了3天
- 把订单号设成int类型,导致后续关联时类型不匹配
- 以为「订单维度表」可以完全替代事实表中的订单号,结果ETL链路断了就全完了
所以我的建议是:
- 业务主键永远保留,哪怕你觉得没用
- 类型用varchar,别用int或bigint
- 属性提取要克制,只保留分析确实需要的字段
- 定期检查退化维度,看看有没有属性被遗漏
3.7 核心逻辑图
下面这张图展示了退化维度的处理流程:
3.8 总结要点
退化维度陷阱的本质,是混淆了「业务标识」和「业务度量」。业务主键是用来标识一件事的,不是用来计算数值的。
记住三条原则:
- 业务主键永远是维度,不是事实
- 保留主键本身,它是数据质量的最后防线
- 属性提取要适度,别为了建模而建模
嗯,这个坑其实很好避免。只要你在设计事实表时,多问自己一句:「这个字段能求和吗?」——答案如果是「不能」,那它大概率是个维度。