数据仓库核心概念:事实表、维度表、粒度、指标、维度属性、星型模型、雪花模型、星座模型
数据仓库建模的本质,是将业务过程转化为可供分析的结构化数据。无论是 Kimball 的维度建模还是 Inmon 的范式建模,最终落地时都离不开一组核心概念:事实表、维度表、粒度、指标、维度属性,以及由它们组合而成的星型模型、雪花模型和星座模型。这些概念不是孤立的术语,而是一套完整的分析语言——事实表回答“发生了什么”,维度表回答“在何时何地由谁发生”,粒度定义“一行代表什么”,指标定义“用什么数值衡量”,维度属性则提供“从哪些角度切分”。本章将逐一拆解这些概念,并重点辨析三种模型的设计取舍与适用场景。
上图展示了这些核心概念之间的逻辑关系:事实表位于中心,通过外键与多个维度表关联;维度表提供维度属性和粒度定义;指标是事实表中的数值列;三种模型则是这些表的不同组织方式。下面逐一深入。
2.1 事实表:业务事件的量化记录
核心原理:事实表是数据仓库中存储业务度量值的表,每一行对应一个业务事件或业务状态。例如,一次销售订单、一次网页点击、一次库存快照。事实表通常包含两类列:外键列(引用维度表)和度量列(数值指标)。事实表的设计遵循“一行一事件”原则,行数增长快,是数据仓库中体量最大的表。
判断方法:识别一张表是否为事实表,可以从三个特征判断:
- 是否包含数值度量:如销售额、数量、金额、时长等。
- 是否包含多个外键:至少一个外键指向维度表。
- 行是否代表事件或快照:而不是描述某个实体的静态属性。
案例拆解:以电商订单为例,订单事实表包含:订单ID(退化维度)、客户ID(外键)、产品ID(外键)、门店ID(外键)、订单日期ID(外键)、订单金额、订单数量、折扣金额。其中“订单金额”是可加指标,“折扣金额”也是可加指标,但“折扣率”是不可加指标(不能直接求和)。
2.2 维度表:分析的角度与上下文
核心原理:维度表是描述业务事件的上下文信息,提供“谁、什么、何时、何地、为何”等属性。维度表通常行数较少(相对事实表),但列数较多,包含大量文本描述和层次关系。维度表的主键被事实表引用为外键。
操作流程:设计维度表时,遵循以下步骤:
- 确定维度来源:从业务系统、主数据管理或外部数据获取。
- 定义维度属性:每个属性应具有业务含义,如“产品类别”“产品品牌”“产品颜色”。
- 建立层次关系:如“产品→子类别→类别→部门”,便于上卷下钻。
- 设置代理键:使用自增整数作为主键,避免依赖业务系统主键(可能变更或复用)。
案例拆解:客户维度表包含:客户代理键(PK)、客户编号(业务键)、客户姓名、性别、年龄、注册日期、会员等级、所在城市、省份、国家。其中“城市→省份→国家”构成地理层次。分析时可按省份汇总销售额,也可按城市下钻。
| 维度属性类型 | 示例 | 用途 |
|---|---|---|
| 标识属性 | 客户编号、SKU | 唯一识别 |
| 描述属性 | 产品名称、颜色 | 报表展示 |
| 层次属性 | 类别→子类别 | 上卷下钻 |
| 退化维度 | 订单号、发票号 | 保留在事实表 |
2.3 粒度:事实表一行代表什么
核心原理:粒度是事实表中一行数据所代表的业务事件的详细程度。粒度决定了事实表的行数和可分析的最低层级。例如,订单级粒度(一行一个订单)、订单行项目级粒度(一行一个订单中的一行商品)、日汇总粒度(一行一个产品一天的总销量)。
判断方法:确定粒度时,回答三个问题:
- 业务事件的最小单位是什么?
- 分析时是否需要下钻到该级别?
- 存储和查询成本是否可接受?
案例拆解:某零售企业需要分析“每个门店每天每种商品的销量”。如果选择订单行项目级粒度,则事实表包含:门店ID、日期ID、商品ID、销售数量、销售金额。每行代表一个商品在一个订单中的销售记录。如果选择日汇总粒度,则每行代表一个商品在一个门店一天的总销量。前者更灵活(可分析单笔订单),但行数多;后者查询快,但丢失订单级信息。
2.4 指标:可量化的业务度量
核心原理:指标是事实表中的数值列,用于衡量业务表现。指标分为三类:
- 可加指标:可以跨任意维度求和,如销售额、数量。
- 半可加指标:只能跨部分维度求和,如库存量(跨时间维度不能求和,但跨产品维度可以)。
- 不可加指标:不能直接求和,如比率、单价、折扣率。
操作流程:定义指标时,需要明确:
- 指标的业务定义(如“销售额”是否含税)。
- 计算公式(如“销售额 = 单价 × 数量”)。
- 聚合方式(SUM、AVG、COUNT、MAX、MIN)。
- 数据来源表与字段。
案例拆解:在销售事实表中,“订单金额”是可加指标,可按月、按产品、按客户求和。“库存数量”是半可加指标,跨时间维度求和无意义(同一商品在不同日期的库存不能相加)。“折扣率”是不可加指标,正确做法是存储“折扣金额”和“原价金额”,在报表层计算折扣率。
| 指标类型 | 示例 | 聚合方式 | 注意事项 |
|---|---|---|---|
| 可加 | 销售额、利润 | SUM | 可跨所有维度 |
| 半可加 | 库存量、账户余额 | SUM(非时间维度) | 时间维度需特殊处理 |
| 不可加 | 单价、转化率 | AVG或重算 | 不能直接SUM |
2.5 维度属性:维度表的血肉
核心原理:维度属性是维度表中的列,提供描述性信息和分类信息。属性决定了分析的灵活性和报表的可读性。例如,日期维度包含年、季度、月、周、日、是否节假日等属性;产品维度包含品牌、类别、颜色、尺寸、上市日期等。
判断方法:评估一个维度属性是否必要,可以从三个角度:
- 分析价值:是否会被用于分组、筛选或报表展示?
- 稳定性:属性值是否频繁变化?如果变化频繁,需要考虑缓慢变化维策略。
- 来源可靠性:数据源是否提供该属性?质量如何?
案例拆解:日期维度是数据仓库中最常用的维度。一个标准的日期维度包含:日期键(PK)、日期、年、季度、月、周、星期几、是否工作日、是否节假日、农历日期(可选)。这些属性支持“按季度对比”“按周分析”“节假日促销效果评估”等分析场景。
2.6 三种模型:星型、雪花、星座
核心原理:这三种模型是维度建模中表关系的组织方式,决定了查询性能、ETL复杂度和可维护性。
对比分析:
| 模型 | 结构特点 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 星型模型 | 事实表居中,维度表直接连接,维度表不规范化 | 查询性能好,SQL简单,易于理解 | 维度表冗余,更新需维护多份 | 大多数OLAP场景,尤其是查询频繁的报表 |
| 雪花模型 | 维度表进一步规范化,拆分为多个关联表 | 减少冗余,节省存储 | 查询需多表JOIN,性能下降,SQL复杂 | 维度层次深且数据量大的场景,或存储成本敏感 |
| 星座模型 | 多个事实表共享维度表 | 维度复用,避免重复建设,支持跨流程分析 | 维度表设计需兼顾多事实表,复杂度高 | 企业级数据仓库,多业务过程(如销售+退货+库存) |
案例拆解:某零售企业同时有“销售事实表”和“退货事实表”。如果采用星型模型,每个事实表都连接独立的客户维度、产品维度、门店维度,导致维度表重复存储。采用星座模型后,两个事实表共享同一套客户维度、产品维度、门店维度,节省存储且保证维度属性一致。但代价是维度表设计需要同时满足两个事实表的分析需求,例如产品维度需要同时包含销售属性和退货属性。
2.7 实战场景:从业务需求到模型落地
场景描述:某连锁餐饮企业需要建立数据仓库,分析各门店的销售、成本、顾客满意度。业务方提出以下需求:
- 按门店、日期、菜品类别分析销售额和销量。
- 按顾客会员等级分析客单价和复购率。
- 对比不同门店的食材成本率。
设计过程:
- 识别业务过程:销售、采购、顾客评价。
- 定义粒度:销售事实表采用订单行项目级粒度(一行一个菜品在一个订单中的销售记录)。
- 设计维度:日期维度、门店维度、菜品维度、顾客维度、订单维度(退化维度)。
- 确定指标:销售额、销量、成本额、顾客评分。
- 选择模型:采用星座模型,销售事实表和成本事实表共享门店维度、日期维度、菜品维度。
实施结果:销售事实表约5000万行,维度表共4张(日期、门店、菜品、顾客)。查询“各门店本月销售额排名”仅需一次JOIN,响应时间小于2秒。成本事实表与销售事实表通过门店+日期+菜品关联,可计算成本率。
复盘经验:
- 粒度定义前置,与业务方确认“一行代表什么”。
- 共享维度表时,属性命名需统一(如“门店ID”在所有事实表中含义一致)。
- 成本事实表与销售事实表粒度不一致(成本按日汇总,销售按订单行),关联时需先聚合到日级别。
2.8 常见误区与设计原则
误区一:事实表中包含文本描述。正确做法:文本放入维度表,事实表只保留外键和数值。
误区二:维度表过度规范化。正确做法:保持维度表扁平,除非维度属性超过100个或存在多值属性。
误区三:忽略粒度声明。正确做法:在事实表注释中明确写明“每行代表一个订单行项目”,并作为ETL校验规则。
误区四:指标定义不统一。正确做法:建立指标字典,明确每个指标的计算公式、聚合方式、数据来源。
设计原则总结:
- 事实表保持原子粒度,不预聚合。
- 维度表提供丰富属性,支持自助分析。
- 优先星型模型,必要时采用星座模型共享维度。
- 指标区分可加、半可加、不可加,存储分子分母而非比率。
- 使用代理键,避免业务键变更导致关联断裂。