粒度选择核心原则:原子粒度、业务过程对齐、不可再分
好,咱们进入正题。事实表粒度怎么选?我这些年踩过的坑,总结下来就三条核心原则。你把这三点吃透了,80% 的场景都不会跑偏。
说白了,粒度选择就是回答一个问题:「这张事实表的一行,到底代表什么?」 答案对了,后面星型模型、汇总表都好办。答案错了,嗯,等着重构吧。
原则一:原子粒度原则
原子粒度,就是最细的、不可拆解的业务事件级别。我习惯叫它「打回原形」原则。
举个例子。你在电商公司,订单表里存了「订单金额 500 元,包含 3 件商品」。这算原子粒度吗?不算。 因为一行里包含了多个商品,你没法单独分析「某件商品的销量」。必须拆成三行,每行一个商品子项。
核心判断标准: 事实表的一行,是否对应业务系统中一次不可再分的操作记录?
我在项目中遇到过最典型的反例:某金融客户把「用户当日所有交易汇总」作为一行存入事实表。结果分析师想查「单笔超过 5 万的交易」,根本查不了。为什么?因为粒度已经被汇总操作污染了。
我的习惯: 设计阶段先问业务方「你们最细的记录长什么样?」拿到原始日志或流水表,再开始建模。不要从汇总需求倒推粒度。
原则二:业务过程对齐原则
这条原则说的是:一张事实表,只服务于一个业务过程。
你想想看,下单是一个过程,支付是另一个过程,退款又是独立的。把它们塞进同一张表,维度关联会乱成一锅粥。我曾经接手过一个遗留系统,一张「交易事实表」里同时包含了下单、支付、发货、签收四个过程的状态字段。结果呢?每次取数都要写一堆 CASE WHEN 来区分当前行到底代表哪个过程,维护成本极高。
正确的做法是拆成多张事实表:
| 业务过程 | 事实表名称 | 粒度描述 |
|---|---|---|
| 用户下单 | 订单事实表 | 每行一个订单商品子项 |
| 支付完成 | 支付事实表 | 每行一次支付流水 |
| 物流发货 | 发货事实表 | 每行一个包裹发货记录 |
| 用户退款 | 退款事实表 | 每行一次退款申请 |
这样做的好处很明显:每张表的维度关系清晰,度量字段纯粹,ETL 逻辑也简单。分析师拿到表就知道「哦,这张表是管支付的」,不用猜。
避坑指南: 我曾经见过有人把「下单」和「支付」强行放在一张表里,用 NULL 表示未支付的行。结果全表扫描时性能极差,而且维度外键经常对不上。记住:一个业务过程一张事实表,别贪心。
原则三:不可再分原则
这条是原子粒度的延伸。它强调:事实表的每一行,在业务语义上不能再往下拆了。
怎么判断?问自己一个问题:如果把这一行再拆成两行,业务上还有意义吗?
举个例子。某零售企业的事实表里有一行:「门店 A 在 2024-01-01 销售了 10 箱矿泉水,总价 200 元」。这能再分吗?可以。如果矿泉水有不同规格(500ml 和 1.5L),那这一行就应该拆成两行。但如果门店的销售系统只记录了「整箱销售」,没有更细的规格信息,那这一行就是不可再分的。
关键判断: 不可再分不是的「物理上不能再分」,而是「业务源系统能提供的最细粒度」。源系统只给到「天」,你就别强行拆到「小时」。
我个人的经验是:先按源系统的最细粒度建模,等业务方提出更细粒度的需求时,再通过增加维度或拆分事实表来扩展。 不要一开始就猜测更细的粒度,那往往会导致过度设计。
三条原则的关系
这三条原则不是孤立的。原子粒度是基础,业务过程对齐是组织方式,不可再分是校验标准。我习惯用一张图来帮团队理解:
从图里你能看到:原子粒度决定了「一行有多细」,业务过程对齐决定了「哪些度量放一起」,不可再分则帮你检查「有没有拆过头或没拆够」。三者形成一个闭环。
实战中的优先级
如果遇到冲突怎么办?比如原子粒度和业务过程对齐看起来矛盾了。我建议的优先级是:
- 先保原子粒度 —— 这是数据可信度的底线。粒度不够细,后续所有分析都受限。
- 再按业务过程拆分 —— 原子粒度确定后,把不同过程的度量分到不同事实表。
- 最后用不可再分校验 —— 检查每张表的每一行是否还能继续拆分。
一个小技巧: 设计完成后,找业务方拿三个最典型的查询需求,用你的事实表去跑一遍。如果都能直接命中粒度,说明选对了。如果某个查询需要先聚合再过滤,那粒度可能偏细了;如果需要先拆解再计算,那粒度可能偏粗了。