











维度表围绕业务过程所处的环境进行设计。
主要包含:
可以理解为:
事实表 = 发生了什么
维度表 = 从什么角度分析
例如订单事实表:
| 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
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。