一、事实表基础概念

说实话,我刚开始做数据仓库那会儿,对事实表的理解也走过弯路。那时候总觉得,不就是一张存数字的表嘛,有什么好研究的?直到被业务部门追着问「为什么这个月的销售额对不上」,我才老老实实把事实表从头啃了一遍。

这一章,我们就从最基础的概念说起。别觉得简单,基础打不牢,后面设计聚合表、处理缓慢变化维度的时候,你会吃大亏。

1.1 什么是事实表

事实表,说白了就是记录「发生了什么」的表。每一行代表一个业务事件,比如一笔订单、一次点击、一通客服电话。

我习惯这样理解:事实表存的是「度量」,维度表存的是「描述」。度量就是那些可以加、减、求平均的数字——销售额、数量、时长、次数。描述就是谁、什么时间、在哪个地点、通过什么渠道。

核心特征:

  • 行数多,增长快——一张订单事实表,一天可能新增几百万行
  • 列数相对少,主要是外键和数值度量
  • 数据一旦写入,很少修改(尤其是交易型事实)

举个例子。你在电商平台买了一部手机,订单金额 5999 元,下单时间是 2024-03-15 14:30:00。这笔记录落到事实表里,大概长这样:

订单ID    | 日期ID | 客户ID | 商品ID | 门店ID | 金额   | 数量
---------|--------|--------|--------|--------|-------|-----
100001   | 20240315 | 8521  | 307    | 12     | 5999  | 1

你看,除了订单ID这个自然键,剩下的全是外键(日期ID、客户ID、商品ID、门店ID)和数值(金额、数量)。这就是事实表的典型结构。

1.2 事实表在星型模型中的位置

星型模型为什么叫「星型」?你想想看,中间一张事实表,周围一圈维度表,用线连起来,是不是像星星?

我画了张图,帮你直观理解这个结构:

星型模型结构示意 事实表 订单事实表 外键 + 度量值 时间维度 年/月/日/周 客户维度 姓名/等级/地区 商品维度 品类/品牌/价格 门店维度 城市/区域/类型 事实表在中心,维度表在四周 通过外键关联,形成星型结构

这张图你应该能看懂。事实表在正中间,四个维度表分布在四周。每个维度表通过一个外键和事实表关联。查询的时候,先过滤维度表,再关联事实表拿度量值——效率很高。

我的经验:星型模型之所以流行,是因为它符合人类的思维习惯。业务人员问「上个月华东区卖了多少台手机」,其实就是「时间维度过滤 + 门店维度过滤 + 商品维度过滤 → 事实表求和」。这种查询路径非常直观。

1.3 事实表与维度表的区别

很多新人分不清事实表和维度表。我刚开始也犯过这个错——把「商品名称」直接塞进事实表,结果事实表膨胀得飞快,查询越来越慢。

这里我整理了一张对比表,帮你一眼看明白:

对比维度 事实表 维度表
核心内容 业务事件的度量值 业务对象的描述属性
数据量 极大,持续增长 相对稳定,变化缓慢
典型字段 外键、数值、时间戳 主键、文本描述、分类属性
更新频率 几乎不更新,只追加 偶尔更新(SCD策略)
查询模式 聚合、求和、平均 过滤、分组、打标签
索引策略 位图索引、分区 B树索引、主键索引

我再给你说个具体的场景。假设你要分析「2024年第一季度,北京地区,高端手机的销售额」。

  • 维度表负责「过滤」:时间维度筛选出 Q1 的日期ID,门店维度筛选出「北京」的门店ID,商品维度筛选出「高端手机」的商品ID。
  • 事实表负责「计算」:拿着这三个ID集合,去事实表里匹配,然后对金额字段求和。

你看,各司其职。维度表告诉你「找什么」,事实表告诉你「找到了多少」。

避坑指南:我曾经接手过一个项目,同事把「客户姓名」直接存在事实表里。理由是「这样查询不用关联维度表,快一些」。结果呢?客户改名字了,事实表里几千万行数据要同步更新,跑了一整夜还没跑完。嗯,从那以后我坚决要求:所有描述属性必须放在维度表,事实表只存外键。

1.4 事实表的粒度概念

聊事实表,绕不开「粒度」这个词。什么叫粒度?就是一行数据代表什么。

  • 最细粒度:一行代表一次原子事件。比如一笔订单中的一件商品(订单行级别)。
  • 粗粒度:一行代表一个汇总结果。比如某天某门店的总销售额。

我个人的习惯是:能细就细,不要提前聚合。为什么?因为细粒度数据可以向上聚合出任何粗粒度结果,但反过来不行。你想想看,如果事实表存的是「每天每家门店的总销售额」,业务突然要分析「每个小时的高峰时段」,你就傻眼了——数据已经被你弄丢了。

粒度选择原则:

  • 交易型事实表 → 最细粒度(单笔交易、单行项目)
  • 周期快照事实表 → 按周期粒度(每天、每月)
  • 累积快照事实表 → 按流程生命周期粒度

这些不同类型的事实表,我们后面章节会详细展开。