由于水平原因,博客大部分内容摘抄于网络,如有错误或者侵权请指出,本人将尽快修改

数仓概论

维度表

维度表概述

维度表围绕业务过程所处的环境进行设计。

主要包含:

  • 主键
  • 维度属性:商品名称、品牌、分类、用户等级等

可以理解为:

事实表 = 发生了什么

维度表 = 从什么角度分析

例如订单事实表:

order_id user_id product_id amount
O001 U001 P001 200

通过 user_id 关联用户维度:

user_id user_name gender level
U001 张三 VIP

就可以分析:

VIP 用户的销售额是多少?


维度表设计步骤

确定维度 → 确定主维表和相关维表 → 确定维度属性

确定维度

事实表有哪些维度,就对应设计哪些维度表。

例如订单事实表:

订单事实表
    ↓
用户维度
商品维度
时间维度
地区维度

如果多个事实表都使用商品维度:

订单事实表 ──┐
支付事实表 ──┼── 商品维度表
销售事实表 ──┘

只创建一张商品维度表,保证维度统一。

如果维度属性非常少,也可以直接放到事实表中:

维度退化

例如订单号本身只有一个简单属性,不值得单独建立维度表,可以直接放事实表。


确定主维表和相关维表

商品维度为例:

商品维度
    ├── sku_info       ← 主维表
    ├── spu_info       ← 相关维表
    ├── brand          ← 相关维表
    ├── category3      ← 相关维表
    ├── category2      ← 相关维表
    └── category1      ← 相关维表

最终把这些信息整合成一张商品维度表


确定维度属性

维度属性就是维度表中的字段。

例如商品维度:

sku_id sku_name brand_name category_name price
P001 Mate 60 华为 手机 4999
P002 iPhone 17 Apple 手机 5999

设计原则:

  • 属性尽可能丰富
  • 尽量使用文字说明
  • 沉淀通用属性

例如不要每次分析都自己拼:

一级分类 + 二级分类 + 三级分类

可以直接在维度表中沉淀:

category_full_name = 手机 > 智能手机 > 华为手机

维度设计要点

规范化与反规范化

规范化 → 雪花模型

把维度拆成多张表:

商品
  ↓
三级分类
  ↓
二级分类
  ↓
一级分类

查询时需要不断 JOIN

反规范化 → 星型模型

把相关信息直接放到商品维度表:

sku_id sku_name category1 category2 category3 brand
P001 Mate 60 数码 手机 智能手机 华为
P002 iPhone 数码 手机 智能手机 Apple

查询时:

少 JOIN、使用简单、查询性能更好

所以数据仓库中的维度表:

一般采用反规范化 → 星型模型


维度变化

维度数据会随着时间发生变化。

例如用户原来是普通会员:

user_id user_name level
U001 张三 普通

后来升级为 VIP:

user_id user_name level
U001 张三 VIP

问题:

以前的“普通会员”状态要不要保留?

数据仓库通常需要保留历史状态。

常见方式:

全量快照表 / 拉链表


全量快照表

每天保存一份完整的维度数据。

例如:

9月18日

user_id user_name level
U001 张三 普通
U002 李四 VIP

9月19日

user_id user_name level
U001 张三 VIP
U002 李四 VIP

优点:

简单、好理解、开发维护成本低

缺点:

数据重复,浪费存储空间


拉链表

拉链表只记录发生变化的历史状态,通过时间范围表示状态的有效期。

例如:

user_id user_name level start_date end_date
U001 张三 普通 09-01 09-18
U001 张三 VIP 09-19 9999-12-31
U002 李四 VIP 09-01 9999-12-31

可以理解成:

U001

普通会员
09-01 ───── 09-18

VIP
09-19 ─────────────────→

核心:

一条记录代表一个历史状态

相比全量快照:

拉链表节省存储空间,更适合变化较少的维度。


多值维度

一条事实记录,对应维度表中的多条记录。

例如:

一个订单包含多个商品。

如果订单事实表粒度是:

一行 = 一个订单

那么:

order_id product_id
O001 P001
O001 P002
O001 P003

一个 order_id 对应多个商品。

解决方案

方案一:降低事实表粒度

从:

一行 = 一个订单

变成:

一行 = 一个订单中的一个商品

order_id product_id quantity
O001 P001 2
O001 P002 1
O001 P003 3

推荐这种方式。


方案二:多个字段保存维度ID

order_id product_id1 product_id2 product_id3
O001 P001 P002 P003

但是:

只适合商品数量固定的情况

所以一般优先:

降低粒度


多值属性

注意和多值维度区分。

多值维度:一条事实对应多个维度记录

多值属性:一条维度记录本身有多个属性值

例如商品 P001:

平台属性:品牌=华为、系统=鸿蒙、CPU=麒麟

方案一:一个字段保存

sku_id platform_attr
P001 品牌:华为,系统:鸿蒙,CPU:麒麟

适合属性数量不固定的情况。

方案二:拆成多个字段

sku_id brand system cpu
P001 华为 鸿蒙 麒麟990
P002 Apple iOS A19

适合:

属性种类固定


总结

维度表
│
├── 怎么设计?
│   └── 维度 → 主维表/相关维表 → 维度属性
│
├── 怎么组织?
│   ├── 规范化 → 雪花模型
│   └── 反规范化 → 星型模型(数仓常用)
│
├── 维度发生变化怎么办?
│   ├── 全量快照 → 每天保存一份
│   └── 拉链表   → 保存历史状态区间
│
├── 一个事实对应多个维度?
│   └── 多值维度 → 优先降低事实表粒度
│
└── 一个维度有多个属性值?
    └── 多值属性 → 一个字段 / 多个字段

事实表

事实表概述

事实表围绕业务过程设计,主要包含:

  • 维度外键:关联维度表
  • 度量值:数量、金额、次数等

特点:

列少、行多、增长快、粒度细


事务型事实表

记录一次业务事件,发生一笔就记录一行。

例如:订单明细

粒度:

一行 = 一个订单中的一个商品

表结构

字段 含义
order_id 订单ID
user_id 用户维度
product_id 商品维度
date_id 日期维度
quantity 商品数量
amount 商品金额

示例数据

order_id user_id product_id date_id quantity amount
O1001 U001 P001 2026-09-18 2 200
O1001 U001 P002 2026-09-18 1 80
O1002 U002 P001 2026-09-18 3 300
O1003 U003 P003 2026-09-19 1 150

可以看到:

发生一笔业务 → 新增一行数据

适合统计:

  • 销售额
  • 销量
  • 订单数
  • 用户购买次数

不足:

  • 不适合直接记录库存、余额等存量指标
  • 如果要计算「下单 → 支付」间隔,需要关联多个事务事实表

周期型快照事实表

按照固定时间周期,记录某一时刻的业务状态。

例如:每日库存快照

粒度:

一行 = 某天 + 某仓库 + 某商品的库存状态

表结构

字段 含义
date_id 日期
warehouse_id 仓库维度
product_id 商品维度
stock_quantity 库存数量

示例数据

date_id warehouse_id product_id stock_quantity
2026-09-18 W001 P001 100
2026-09-18 W001 P002 50
2026-09-19 W001 P001 80
2026-09-19 W001 P002 45

这里不是记录「库存发生了什么变化」,而是记录:

每天结束时,库存是多少

事实类型

可加事实

所有维度都可以累加。

例如:

销售数量 = 10 + 20 + 30

半可加事实

部分维度可以累加,但不能跨时间累加

例如库存:

9月18日库存 100
9月19日库存 80
❌ 不能说库存 = 180

不可加事实

不能直接进行加法。

例如:

毛利率、转化率、平均价格

通常需要保存分子 + 分母,再计算比例。


累积型快照事实表

记录一个业务流程中的多个关键节点。

例如:订单生命周期

业务流程:

下单 → 支付 → 发货 → 收货

粒度:

一行 = 一个订单

表结构

字段 含义
order_id 订单ID
user_id 用户维度
product_id 商品维度
order_date 下单时间
pay_date 支付时间
delivery_date 发货时间
receive_date 收货时间
amount 订单金额

示例数据

order_id user_id product_id order_date pay_date delivery_date receive_date amount
O1001 U001 P001 09-18 10:00 09-18 10:05 09-18 15:00 09-20 12:00 200
O1002 U002 P002 09-18 11:00 09-18 11:10 09-19 09:00 09-21 14:00 80
O1003 U003 P003 09-19 09:00 09-19 09:03 NULL NULL 150

这样可以直接计算:

支付耗时 = pay_date - order_date

发货耗时 = delivery_date - pay_date

总履约时间 = receive_date - order_date

最大的特点:

多个业务节点放在同一行

所以不用再把「订单表、支付表、发货表、收货表」几个大表进行关联。


看到这些关键词 优先考虑
下单、支付、退款、销售 事务型
每天、每月、期末、库存、余额 周期型
下单→支付→发货→收货、耗时、周期 累积型

三种事实表对比

类型 一行代表什么 典型例子 核心特点 关键字
事务型 一次业务事件 订单明细 发生一次,记录一次 下单、支付、退款、销售
周期型快照 某时间点的状态 每日库存 定期记录状态 每天、每月、期末、库存、余额
累积型快照 一个业务流程 订单生命周期 多个节点放一行 下单→支付→发货→收货、耗时、周期

数仓分层

flowchart TB subgraph APP["数据应用层"] ADS["ADS<br/>Application Data Service<br/><br/>面向业务应用、报表、数据服务"] end subgraph SUMMARY["汇总数据层"] DWS["DWS<br/>Data Warehouse Summary<br/><br/>按主题域汇总<br/>沉淀公共指标"] end subgraph DETAIL["明细数据层"] DWD["DWD<br/>Data Warehouse Detail<br/><br/>数据清洗、转换、标准化<br/>形成统一明细数据"] end subgraph RAW["原始数据层"] ODS["ODS<br/>Operation Data Store<br/><br/>保存源系统原始数据<br/>尽量保持源数据结构"] end subgraph COMMON["公共维度层"] DIM["DIM<br/>Dimension<br/><br/>统一维度定义<br/>时间 / 地区 / 组织 / 商品等"] end ODS -->|"清洗、标准化"| DWD DWD -->|"主题汇总、指标加工"| DWS DWS -->|"应用加工、数据服务"| ADS DIM -.->|"维度关联"| DWD DIM -.->|"维度关联"| DWS style RAW fill:#edf6e8,stroke:#5b8c3a style DETAIL fill:#e3f0d9,stroke:#5b8c3a style SUMMARY fill:#d2e8bd,stroke:#5b8c3a style APP fill:#b7dc8a,stroke:#5b8c3a style COMMON fill:#dfead8,stroke:#5b8c3a
posted @ 2026-09-19 09:07  小纸条  阅读(4)  评论(0)    收藏  举报