4、数据建模入门:星型模型与雪花模型、事实表与维度表设计、度量与维度概念
好,咱们进入数据建模这个环节。说实话,很多做OA报表的同学,一开始都栽在“表怎么设计”这个问题上。我当年刚接手第一个报表项目时,也是直接对着业务表一顿JOIN,结果查询慢得像蜗牛爬,还被领导点名批评。后来我才明白——报表的数据,不能直接从业务库拉,得先建模。
今天这一讲,咱们就把数据建模最核心的几个概念掰开揉碎。你只要搞懂星型模型、雪花模型、事实表和维度表,后面做报表就会顺手很多。
4.1 事实表与维度表:报表的两大基石
先问个问题:你平时做报表,最关心什么?
无非是两件事:“发生了什么” 和 “从哪些角度去看”。
事实表,就是记录“发生了什么”的表。比如OA系统里的审批记录、考勤打卡、报销单——每一行都是一条具体的事件。事实表里存的是数字,比如金额、次数、时长,这些我们叫度量。
维度表,就是提供“看的角度”的表。比如部门表、员工表、时间表、审批类型表。维度表里存的是文本描述,比如部门名称、员工姓名、日期,这些我们叫维度。
核心口诀:
- 事实表:记录“发生了什么”,存数字(度量)
- 维度表:描述“从哪个角度看”,存文本(维度)
举个例子。你做一个“各部门月度审批量”报表:
- 事实表:审批记录表。每一行是一条审批,字段包括审批ID、审批时间、审批人ID、审批类型ID、审批时长(分钟)。这里的“审批时长”就是度量。
- 维度表:部门表(部门ID、部门名称、部门负责人)、时间表(日期、月份、季度、年份)、审批类型表(类型ID、类型名称)。
你看,事实表通过外键(部门ID、时间ID)关联到维度表。这样你就能从“部门”和“时间”两个维度,去统计“审批时长”这个度量了。
我的经验:设计事实表时,尽量只存数字和ID,别存文本描述。文本描述放在维度表里。这样事实表会非常“瘦”,查询速度会快很多。我曾经见过有人把部门名称直接写在事实表里,结果数据量一上来,查询慢到怀疑人生。
4.2 星型模型:最常用的报表模型
星型模型,说白了就是一张事实表在中间,周围围着多张维度表。形状像星星,所以叫星型模型。
为什么OA报表里星型模型最常用?因为简单、查询快。你想想看,事实表只跟维度表做一次JOIN,不需要多层嵌套。数据库优化器最喜欢这种结构了。
我举个例子。假设你要做一个“员工考勤汇总报表”:
- 事实表:考勤记录表。字段:考勤ID、员工ID、日期ID、出勤状态ID、迟到分钟数、早退分钟数。
- 维度表:员工维度表(员工ID、姓名、部门、职位)、时间维度表(日期ID、年、月、日、星期)、出勤状态维度表(状态ID、状态名称:正常、迟到、早退、缺勤)。
查询“销售部3月份的迟到总分钟数”时,SQL大概长这样:
SELECT
d.dept_name,
SUM(f.late_minutes) AS total_late_minutes
FROM fact_attendance f
JOIN dim_employee e ON f.employee_id = e.employee_id
JOIN dim_date d ON f.date_id = d.date_id
WHERE e.dept_name = '销售部'
AND d.year = 2025
AND d.month = 3
GROUP BY d.dept_name;
你看,就一次JOIN,清晰明了。星型模型的好处就在这里——查询路径短,性能好。
星型模型的特点:
- 一张事实表,多张维度表
- 维度表不关联其他维度表(都是直接连事实表)
- 查询时JOIN次数少,性能高
- 适合大多数OA报表场景
4.3 雪花模型:更规范,但查询稍慢
雪花模型,是星型模型的“升级版”。它把维度表进一步拆分成更细的维度表。形状像雪花,所以叫雪花模型。
举个例子。在星型模型里,员工维度表可能直接包含“部门名称”和“部门负责人”。但在雪花模型里,你会把“部门”单独拆成一张维度表,员工表只存部门ID。这样数据更规范,没有冗余。
但代价是什么?查询时要多JOIN一次。比如查“销售部3月份的迟到总分钟数”,在雪花模型下,SQL会变成:
SELECT
d.dept_name,
SUM(f.late_minutes) AS total_late_minutes
FROM fact_attendance f
JOIN dim_employee e ON f.employee_id = e.employee_id
JOIN dim_department d ON e.dept_id = d.dept_id
JOIN dim_date dt ON f.date_id = dt.date_id
WHERE d.dept_name = '销售部'
AND dt.year = 2025
AND dt.month = 3
GROUP BY d.dept_name;
多了一次JOIN(员工表→部门表)。数据量小的时候感觉不到,但一旦事实表有几百万行,多一次JOIN可能就是几秒的差距。
避坑指南:我曾经在一个OA项目里,为了追求“数据规范”,把所有维度都拆成了雪花模型。结果报表查询平均慢了3倍。后来我果断改回星型模型,把部门名称、负责人这些字段直接冗余到员工维度表里。查询速度立马回来了。所以我的建议是:OA报表场景,优先用星型模型。除非你的维度表本身非常大(比如几十万行),才考虑雪花模型。
4.4 度量与维度:报表的灵魂
咱们再深入聊聊度量和维度这两个概念。说白了:
- 度量:就是你要“算”的东西。比如金额、次数、时长、人数。度量通常是数字,可以进行SUM、AVG、COUNT等聚合操作。
- 维度:就是你要“看”的角度。比如时间、部门、员工、审批类型。维度通常是文本或分类数据,用来做分组、筛选、钻取。
我举个例子。在OA系统的“报销分析报表”里:
- 度量:报销金额、报销笔数、平均报销金额
- 维度:报销月份、报销部门、报销类型(差旅、办公、招待)
你想想看,报表的本质是什么?就是从不同维度去聚合度量。没有维度,度量就是一堆没意义的数字。没有度量,维度就是一堆空标签。
设计时的黄金法则:
- 度量放在事实表里,尽量用数值类型(INT、DECIMAL)
- 维度放在维度表里,尽量用文本或枚举类型
- 一个事实表至少要有1个度量,1个维度(否则就不是报表了)
4.5 实战:设计一个OA审批报表模型
咱们来动手设计一个。假设你要做一个“各部门审批效率分析”报表,需要统计:
- 每个部门的平均审批时长
- 每个审批类型的审批数量
- 每个月的审批完成率
按照我们刚才的思路:
第一步:确定事实表
事实表叫 fact_approval,字段如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| approval_id | INT | 审批ID(主键) |
| employee_id | INT | 员工ID(外键→维度表) |
| date_id | INT | 日期ID(外键→维度表) |
| type_id | INT | 审批类型ID(外键→维度表) |
| approval_hours | DECIMAL(10,2) | 审批时长(小时)—— 度量 |
| is_completed | INT | 是否完成(1=是,0=否)—— 度量 |
第二步:确定维度表
维度表有三张:
dim_employee:员工ID、姓名、部门ID、部门名称、职位dim_date:日期ID、年、月、日、季度、星期dim_approval_type:类型ID、类型名称(请假、报销、采购等)
第三步:确定模型
这里我建议用星型模型。因为维度表都不大,直接关联事实表即可。查询“销售部3月份的平均审批时长”时,SQL就是:
SELECT
e.dept_name,
AVG(f.approval_hours) AS avg_approval_hours
FROM fact_approval f
JOIN dim_employee e ON f.employee_id = e.employee_id
JOIN dim_date d ON f.date_id = d.date_id
WHERE e.dept_name = '销售部'
AND d.year = 2025
AND d.month = 3
GROUP BY e.dept_name;
我的习惯:设计事实表时,我会把所有的外键和度量放在一起,字段名用下划线分隔,比如 employee_id、approval_hours。维度表的字段名则尽量用中文别名(在报表工具里显示友好)。这样后期维护时,一看字段名就知道是事实还是维度。
4.6 总结:记住这三句话
好了,这一讲的内容就这些。我帮你总结一下:
- 事实表存度量,维度表存维度——这是建模的根基。
- OA报表优先用星型模型——简单、快、好维护。雪花模型除非必要,否则别碰。
- 设计时先想清楚“要算什么”和“从哪看”——度量决定了事实表,维度决定了维度表。
下一讲,咱们会深入ETL流程,讲讲怎么把业务数据清洗成事实表和维度表。到时候我会分享一个我踩过的坑——数据重复导致报表翻倍,那叫一个惨。咱们下节课见。