维度建模核心概念:事实表、维度表、度量与粒度
说实话,维度建模这个概念,我入行头三年一直觉得它很玄乎。直到有一次,我接手一个零售数据仓库项目,业务方天天抱怨报表对不上——销售额总和跟财务系统差了 0.5 个亿。我排查了三天,最后发现是事实表里混进了不可加事实,直接求和当然会出问题。嗯,从那以后,我对维度建模的每个细节都不敢马虎了。
今天咱们就把维度建模的四个核心概念掰开揉碎讲清楚。你想想看,理解了这些,后面建模基本就顺了。
事实表:业务事件的数字记录
事实表,说白了就是记录「发生了什么」的表。每一行代表一个业务事件,比如一笔订单、一次点击、一通客服电话。事实表里主要放的是度量值——那些可以算的数字。
但度量值不是都能随便加的。我习惯把它们分成三类:
| 类型 | 定义 | 举例 | 能不能跨维度求和 |
|---|---|---|---|
| 可加事实 | 可以沿任意维度求和 | 销售额、订单数量、库存数量 | 能 |
| 半可加事实 | 只能沿部分维度求和 | 账户余额、库存水位、温度 | 不能沿时间维度求和 |
| 不可加事实 | 任何维度都不能求和 | 单价、比率、折扣率 | 不能 |
关键点:可加事实是事实表的主力军。半可加事实要小心处理,比如账户余额按时间求和毫无意义——你不可能把1月余额和2月余额加起来说这是「总余额」。不可加事实通常用平均值或计数来处理。
我在项目中遇到过最典型的坑:有人把「折扣率」直接求和,然后除以行数算平均。其实应该用加权平均,以销售额为权重。否则结果完全失真。
维度表:业务描述的百科全书
维度表回答的是「谁、什么、何时、何地、为何」这些问题。它给事实表提供上下文。比如产品维度告诉你这个商品叫什么、属于哪个品类、价格带是多少。
维度表里有两个特殊概念,我重点说说:
退化维度
退化维度,就是那些没有独立维度表、直接放在事实表里的维度属性。比如订单号、发票号。为什么这么做?因为这些维度属性没有其他描述信息,单独建一张表就太浪费了。
举个例子:订单号在订单事实表里直接存着就行。你不需要建一张「订单号维度表」,因为订单号本身没有其他属性可描述。我早期做项目时,啥都想建维度表,结果维度表比事实表还多,查询性能反而下降了。
我的经验:判断一个字段要不要做成退化维度,就看它有没有超过 3 个描述属性。没有?直接放事实表里。有?建维度表。
缓慢变化维度(SCD)
维度属性会变,比如客户搬家了、产品改名字了。怎么处理这些变化?有三种经典策略:
- SCD 类型 1(覆盖):直接更新旧值。简单,但丢失历史。适合「错误修正」场景。
- SCD 类型 2(新增行):保留旧记录,插入新记录,用生效日期和失效日期标记。保留完整历史,但维度表会膨胀。我最常用这个。
- SCD 类型 3(新增列):加一列存旧值。适合只关心上一次变化的情况,比如「前一个部门」。
我曾经犯过一个错:对客户地址用了类型 1,结果财务要按历史地址统计区域销售额,数据全对不上了。后来改成类型 2,虽然表大了点,但历史查询再也没出过问题。
度量与粒度:先定粒度,再谈度量
粒度,就是事实表里每一行代表什么。这是建模的第一步,也是最容易出错的一步。
我习惯这样问业务方:「你这条记录,是每笔交易一条,还是每天汇总一条?」答案就是粒度。
常见的粒度级别:
- 事务粒度:每发生一次事件就一条记录。比如每笔订单、每次点击。最细,最灵活。
- 周期快照粒度:按固定时间间隔记录状态。比如每天记录一次库存量。适合分析趋势。
- 累积快照粒度:记录一个流程从开始到结束的全过程。比如订单从下单到发货到签收。适合流程分析。
注意:粒度一旦确定,事实表里的度量就必须跟这个粒度对齐。比如粒度是「每笔订单」,那度量就不能是「每日销售额」。否则数据会重复或遗漏。
度量就是事实表里的数字列。它必须跟粒度一一对应。举个例子:订单事实表里,订单金额 是度量,它跟每笔订单对应。如果你还想分析「客户总消费金额」,那得在汇总层算,不能直接塞进事务粒度的事实表里。
星型模型与雪花模型
这两个模型,说白了就是维度表怎么组织的问题。
星型模型:所有维度表直接连到事实表,像星星一样散开。维度表不关联其他维度表。查询时只需要一次 join,性能最好。我 80% 的项目都用星型模型。
雪花模型:维度表可以再关联子维度表,像雪花一样分叉。比如产品维度表关联品类维度表,品类维度表再关联部门维度表。这样更规范化,减少了数据冗余,但查询时要多 join 几次,性能会下降。
我个人习惯:能用星型就别用雪花。除非你存储成本极其敏感,或者维度层次非常深(比如 5 层以上),否则星型模型在查询性能上的优势远大于那点存储节省。
下面这张图展示了两种模型的结构对比:
你看,星型模型里所有维度表都直接挂在事实表下,查询时只需要一次 join。雪花模型里维度表还有子维度,查询路径更长。实际项目中,我建议优先选星型,除非你遇到以下情况:
- 维度层次很深(比如地理维度:国家→省→市→区),用雪花可以减少冗余
- 存储成本是硬约束,必须规范化
- ETL 处理维度变化时,雪花模型更容易维护
我的建议:先按星型建模,等遇到性能或存储瓶颈时,再考虑局部雪花化。别一开始就搞雪花,否则查询优化会很难受。
好了,维度建模的核心概念就这些。记住:事实表管度量,维度表管描述,粒度定精度,模型定结构。把这四个点吃透了,后面建模就是搭积木的事。