维度表是数据仓库描述性的核心。事实表通过数值记录业务事件的量级,而维度表则提供了解释这些数字所需的信息。如果没有完善的维度,事实表就只是一堆无意义的数字。以下是对构建支持分析查询中直观筛选、分组和标记的有效维度表的结构要求进行审视。信息在分析中的作用精心设计的维度表回答了业务事件的“谁、什么、哪里、何时、为什么以及如何”。如果事实表记录了一笔销售,那么维度表会描述购买商品的顾客、销售的产品、商店位置以及促成该销售的促销活动。构建这些表的主要目的是为数据分析师提供便于使用的界面。当分析师编写查询或使用BI工具时,他们几乎完全通过维度属性来限制和分组他们的数据。因此,维度表包含大量文本的描述性属性,而不是缩写代码或ID。例如,操作型数据库可能会将状态存储为“S”或“P”。在维度表中,此属性应扩展为“已发货”或“待处理”。这种详细的方法减轻了分析师的认知负担,消除了在构建查询时查找对应代码的需要。维度表的结构组成维度表的典型特征是“宽而浅”。与事实表相比,它们包含的行数相对较少(数千或数百万行,而事实表可能有数十亿行),但拥有许多列。每列代表所建模实体的特定属性。标准维度表由三种主要组成部分构成:主键(代理键): 一个完整的数据仓库通常会分配一个系统生成的整数来唯一标识每个维度行。这使得数据仓库与源系统键生成逻辑的变化隔离。自然键: 来自操作型源系统的原始标识符(例如,customer_id,product_sku)。这些保留在表中,以方便ETL更新和交叉引用。描述性属性: 用于筛选和分组的文本字段。请考虑以下产品维度表的结构。它整合了操作型系统中可能分散在五六个范式化表中的数据。digraph G { rankdir=TB; node [shape=box, style=filled, fontname="Helvetica", fontsize=10]; subgraph cluster_0 { label = "非范式化产品维度"; style = filled; color = "#f8f9fa"; node [color="#228be6", fillcolor="#e7f5ff"]; ProductDim [label=< <table border="0" cellborder="1" cellspacing="0"> <tr><td bgcolor="#228be6"><font color="white"><b>产品维度表</b></font></td></tr> <tr><td align="left">产品键 (主键)</td></tr> <tr><td align="left">产品SKU (自然键)</td></tr> <tr><td align="left">产品名称</td></tr> <tr><td align="left">品牌名称</td></tr> <tr><td align="left">类别名称</td></tr> <tr><td align="left">部门名称</td></tr> <tr><td align="left">供应商名称</td></tr> <tr><td align="left">颜色</td></tr> <tr><td align="left">尺寸</td></tr> <tr><td align="left">重量 (公斤)</td></tr> <tr><td align="left">是否活跃</td></tr> </table> >]; } }非范式化维度表的结构将属性整合到单一视图中,简化了查询的访问路径。非范式化和层级扁平化在操作型数据库(OLTP)中,设计侧重于通过范式化消除冗余。如果一个产品属于一个类别,而该类别又属于一个部门,范式化设计会创建三个独立的表。要获取产品的部门信息,必须执行两次连接。在维度建模中,我们优先考虑查询性能和易用性,而非存储效率。我们通过将这些层级“扁平化”到一个表中,特意引入冗余。这个过程被称为非范式化。当您扁平化一个层级时,维度粒度(例如产品)与其父级(类别、部门)之间的关系在行结构中变得明确。范式化(OLTP)方法: $$产品 \xrightarrow{连接} 类别 \xrightarrow{连接} 部门$$维度化(OLAP)方法: $$产品维度表(产品, 类别, 部门)$$这种扁平化允许分析师按层级中的任何级别进行分组,而无需管理复杂的连接。例如,按部门计算销售额在产品维度上变为简单的 GROUP BY department_name,而不是遍历多个表。属性与基数在为维度选择属性时,考虑其基数(即列中唯一值的数量)会有帮助。高基数列是唯一或接近唯一的标识符(如电话号码),而低基数列代表分组(如通用颜色或州)。分析查询通常在低基数列上筛选时表现最佳。然而,维度表必须同时处理这两种情况。下图展示了维度表中属性基数的典型分布。虽然主键是唯一的(100%基数),但大多数描述性属性在多行中共享值,从而创建用于聚合的分组。{ "data": [ { "x": ["主键", "用户ID", "邮政编码", "城市", "州/省", "性别", "状态"], "y": [10000, 10000, 850, 120, 50, 3, 2], "type": "bar", "marker": { "color": ["#339af0", "#4dabf7", "#74c0fc", "#a5d8ff", "#d0ebff", "#e7f5ff", "#e7f5ff"] } } ], "layout": { "title": "万行维度中的属性基数", "xaxis": {"title": "属性类型"}, "yaxis": {"title": "唯一值数量(基数)", "type": "log"}, "height": 400, "margin": {"l": 50, "r": 50, "t": 50, "b": 50} } }属性间的基数分布差异显著。请注意对数刻度;分组属性(州/省、性别)的唯一值数量远少于标识符。处理空值和未知成员维度表结构中一个常见问题是处理缺失数据。在事务流中,您可能会遇到 customer_id 缺失或无效的记录。在关系型数据库中,可以将外键留空(NULL)。然而,在维度模型中,事实表和维度表之间的引用完整性是严格的。如果事实行具有 NULL 外键,它可能会被执行内连接的查询排除在外,导致聚合总数不正确。为解决这个问题,维度表应包含特定的行来处理“未知”、“不适用”或“迟到”的数据。这些行通常带有负整数代理键(例如 -1、-2),以将它们与有效的系统键区分开。实施策略:ID -1(未知): 当源系统发送NULL或无效标识符时使用。ID -2(不适用): 当信息不适用于特定事务时使用(例如,针对没有促销活动的销售的“促销维度”)。这种做法确保 SUM(Sales) 返回相同的全局总数,无论用户如何切分数据。标志和指示器模式维度表通常包含布尔型标志(真/假)。为了使它们更便于报表使用,标准做法是将隐晦的布尔代码转换为可读的文本字符串。与其使用包含 0 或 1 的 is_corporate_client 列,不如使用包含“企业”和“零售”值的 client_segment 列来构建维度。这避免了分析师在报表层使用 CASE 语句来解码标志含义的需要。如果多个二进制标志经常一起出现,并且它们代表互斥状态,可以考虑将它们组合成一个“垃圾维度”或一个描述性状态列。这使主维度表保持整洁,并专注于主要实体属性。