了解星型架构及其对 Power BI 的重要性
本文面向 Power BI Desktop 数据建模者。 它介绍星型架构设计及其与开发针对性能和可用性优化的 Power BI 语义模型的相关性。
重要
Power BI 语义模型依赖于 Power Query 导入或连接到数据。 这意味着必须使用 Power Query 来转换和准备源数据,这在拥有大量数据时可能很困难,或者需要实现诸如渐变维度等高级概念(本文稍后所述)。
在遇到这些挑战时,建议首先开发数据仓库并提取、转换和加载(ETL)过程来定期加载数据仓库。 然后,语义模型可以连接到数据仓库。 有关详细信息,请参阅 Microsoft Fabric Warehouse 中的维度建模。
提示
本文并不旨在提供有关星型架构设计的完整讨论。 有关更多信息,请直接参考广泛采用的已发布内容,如 Ralph Kimball 等创作的数据仓库工具包:维度建模的最终指南(第三版,2013 年)。
星型架构概述
星型架构是由关系数据仓库广泛采用的成熟建模方法 。 它要求建模者将其模型表分类为“维度”或“事实” 。
- 维度表:描述了业务实体(即建模对象 )。 实体可以包含产品、人员、地点和包括时间本身的概念。 在星型架构中,最一致的表是日期维度表。 维度表包含充当唯一标识符和其他列的键列(或列)。 其他列支持筛选和分组数据。
- 事实数据表 存储观察或事件,可以是销售订单、库存余额、汇率、温度等。 事实表包含与维度表相关的维度键列和数值度量列。 维度键列确定事实数据表的维度,而维度键值确定事实数据表的粒度 。 例如,假设一个事实数据表旨在存储具有两个维度键列
Date
和ProductKey
. 很容易理解该表有两个维度。 但若不考虑维度键值,就无法确定粒度。 在此示例中,请考虑列中存储Date
的值是每月的第一天。 在这种情况下产生的是月份-产品级别的粒度。
通常情况下,维度表包含的行数相对较少。 另一方面,事实数据表可以包含大量行,并随着时间的推移继续增长。
规范化与非规范化
若要理解本文中描述的一些星型架构概念,请务必了解以下两个术语:规范化和非规范化。
规范化这一术语用于描述以减少重复数据的方式存储的数据。 请考虑具有唯一键值列的产品表,例如产品键,以及描述产品特征的其他列,例如产品名称、类别、颜色和大小。 当销售表仅存储密钥(如产品密钥)时,该表被视为是规范化的。 在下图中,请注意,只有 ProductKey
列记录产品。
然而,如果除了密钥,销售表还存储了产品详细信息,则该表被视为是非规范化的。 在下图中,请注意 ProductKey
,其他与产品相关的列记录产品。
从导出文件或数据提取中获取数据时,它可能表示一组非规范化数据。 在这种情况下,请使用 Power Query 将源数据转换为多个规范化表。
如本文所述,应努力开发具有表示规范化事实和维度数据的表的优化 Power BI 语义模型。 但是,有一个例外是 雪花维度 可能非规范化,以便生成单个模型表。
与 Power BI 语义模型的星型架构相关性
本文中引入的星型架构设计和许多相关概念与开发针对性能和可用性优化的 Power BI 模型紧密相关。
假设每个 Power BI 报表视觉对象都会生成发送到 Power BI 语义模型的查询。 通常,查询筛选、分组和汇总模型数据。 设计良好的模型则提供用于筛选、分组和汇总的表。 此设计非常符合星型架构原则:
- 维度表支持 筛选 和 分组。
- 事实数据表启用 汇总。
没有建模器设置为将表类型设置为维度或事实的表属性。 它实际上是由模型关系决定的。 模型关系在两个表之间建立筛选器传播路径,它是确定表类型的关系的基数属性。 常见关系基数为“一对多” 或反过来的“多对一” 。 “一”端始终是维度表,而“多”侧始终是事实数据表。
结构良好的模型设计包括维度表或事实数据表的表。 请避免对一个表混用这两种类型。 我们还建议你努力提供正确数量的表,并就地提供正确的关系。 事实数据表始终以一致的粒度加载数据也很重要。
最后,请务必了解,优化模型设计是一门科学,也是一门艺术。 有时可以在得到良好指导的情况进行有意义的突破。
有许多概念与星型架构设计相关,可应用于 Power BI 语义模型。 这些概念包括:
度量值
在星型架构设计中,度量值是存储要汇总的值的事实数据表列 。 在 Power BI 语义模型中,度量值具有不同的(但类似的)定义。 模型支持显式度量值和隐式度量值。
- 显式度量 值是明确创建的,它们基于用 数据分析表达式(DAX) 编写的公式来实现汇总。 度量表达式通常使用 DAX 聚合函数(例如
SUM
、MAX
MIN
、AVERAGE
和其他人)在查询时生成标量值结果(从不存储在模型中的值)。 度量值表达式涵盖广泛,从简单的列聚合到更复杂的公式(覆盖筛选器上下文和/或关系传播的公式)应有尽有。 有关详细信息,请阅读 Power BI Desktop 中的 DAX 基础知识。 - 隐式度量值 是由报表视觉对象或 Q&A 汇总的列。 它们为模型开发人员提供了方便,因为在许多情况下,无需创建(显式)度量值。 例如,Adventure Works 经销商销售
Sales Amount
列可以通过多种方式(总和、计数、平均值、中值、最小值、最大值等)进行汇总,而无需为每个可能的聚合类型创建度量值。
在 “数据 ”窗格中,显式度量值由计算器图标表示,而隐式度量值由 sigma 符号(∑)表示。
但是,创建度量值的原因有三个令人信服的原因,即使对于简单的列级摘要也是如此:
如果知道报表作者将使用 多维表达式(MDX)查询语义模型,该模型必须包含显式度量值。 这是因为 MDX 无法实现列值的汇总。 值得注意的是,在 Excel 中执行分析时使用 MDX,因为数据透视表发出 MDX 查询。
当你知道报表作者将使用 MDX 查询设计器创建 Power BI 分页报表时,语义模型必须包含显式度量值。 只有 MDX 查询设计器支持服务器聚合。 因此,如果报表作者需要通过 Power BI(而不是通过分页报表引擎)计算度量值,就必须使用 MDX 查询设计器。
想要控制报表作者如何以特定方式汇总列。 例如,可以汇总经销商销售
Unit Price
列(表示每单位费率),但只能使用特定的聚合函数。 不应对它求和,但应该使用其他聚合函数(如 min、max 或 average)进行汇总。 在此实例中,建模器可以隐藏Unit Price
列,并为所有适当的聚合函数创建度量值。这种设计方法非常适用于在 Power BI 服务中创作的报表以及问答。 但是,Power BI Desktop 实时连接 允许报表作者在 “数据 ”窗格中显示隐藏字段,这可能会导致规避此设计方法。
代理键
代理键是添加到表中以支持星型架构建模的一种唯一标识符 。 根据定义,它不会在源数据中进行定义或存储。 一般而言,代理键将添加到关系数据仓库维度表中,以便为每个维度表行提供唯一标识符。
Power BI 语义模型关系基于一个表中的单个唯一列,该列将筛选器传播到不同表中的单个列。 当语义模型中的维度表不包含单个唯一列时,必须添加唯一标识符才能成为关系的“一方”。 在 Power BI Desktop 中,可以通过添加 Power Query 索引列来实现此要求。
必须将此查询与“多”那一方的查询合并,以便还能向其中添加索引列。 将这些查询加载到语义模型时,可以在模型表之间创建一对多关系。
雪花维度
雪花维度是单个业务实体的一组规范化表 。 例如,Adventure Works 按类别和子类别将产品分类。 产品分配给子类别,而子类别又相应地分配给类别。 在 Adventure Works 关系数据仓库中,产品维度经过了规范化并存储在三个相关表中:DimProductCategory
、DimProductSubcategory
和 DimProduct
。
请想象这些规范化表位于事实数据表之外,形成雪花形状的设计。
在 Power BI Desktop 中,可以选择模拟雪花维度设计(可能是因为源数据确实存在),或合并源表以形成单个非规范化模型表。 一般而言,单个模型表的优点比多个模型表的优点更多。 最理想的决策取决于数据量和模型的可用性要求。
在选择模拟雪花维度设计时:
- Power BI 加载更多的表,从存储和性能角度而言,这样效率较低。 这些表必须包括支持模型关系的列,因此可能会导致模型较大。
- 需要遍历较长的关系筛选器传播链,这可能比应用于单个表的筛选器效率低。
- “ 数据 ”窗格向报表作者提供了更多的模型表,这可能会导致不太直观的体验,尤其是在雪花维度表仅包含一两列时。
- 无法创建包含来自多个表的列的层次结构。
在选择集成到一个模型表中时,还可以定义一个层次结构,其中包含维度的最高和最低粒度。 可能,冗余非规范化数据的存储可能会导致模型存储大小增加,尤其是对于大型维度表。
渐变维度
渐变维度(或 SCD)是一种在一段时间内适当管理维度成员更改的维度成员。 当业务实体值以计划外的方式缓慢变化时,它适用。 SCD 的一个很好的示例是客户维度,因为它的联系人详细信息列(例如电子邮件地址和电话号码很少更改)。 相比之下,当维度属性经常变化(如股票的市场价格)时,某些维度被认为是 快速 变化的。 这些实例中的常见设计方法是在事实数据表度量值中存储快速变化的属性值。
星型架构设计理论指两种常见的 SCD 类型:类型 1 和类型 2。 维度表可以是类型 1 或类型 2,也可以同时支持不同列的两种类型。
类型 1 SCD
类型 1 SCD 始终反映最新值,当检测到源数据有更改时,维度表数据会被覆盖。 这种设计方法常用于存储附属值的列(例如客户的电子邮件地址或电话号码)。 当客户电子邮件地址或电话号码更改时,维度表会使用新值更新客户行。 就好像客户的联系信息一直是最新值一样。
Power BI 模型维度表的非增量刷新可实现类型 1 SCD 的结果。 它刷新表数据以确保加载最新值。
类型 2 SCD
类型 2 SCD 支持维度成员的版本控制。 如果源系统未存储版本,通常是数据仓库加载过程,用于检测更改并适当管理维度表中的更改。 在这种情况下,维度表必须使用代理键来提供对维度成员的某个版本的唯一引用 。 它还包括定义版本的日期范围有效性的列(如 StartDate
和 EndDate
),可能还包括标志列(如 IsCurrent
),以便轻松地按当前维度成员进行筛选。
例如,Adventure Works 将每个销售人员分配到销售区域。 当销售人员重定位区域时,必须创建新的销售人员版本,以确保历史记录事实与前一个区域保持关联。 为了能按销售人员准确地进行销售历史分析,维度表必须存储销售人员及其关联区域的各个版本。 该表还应包含开始和结束日期值,以定义有效期。 当前版本可能定义空结束日期(或 9999/12/31),这表示该行是当前版本。 该表还必须具有代理项密钥,因为业务密钥(在此实例中,员工 ID)不会是唯一的。
请务必了解,当源数据不存储版本时,必须使用中间系统(如数据仓库)来检测和存储更改。 表加载过程必须保留现有数据并检测更改。 当检测到更改时,表加载过程必须让当前版本变为过期版本。 它从上个 EndDate
值开始,更新 EndDate
值并插入新版本的 StartDate
值,从而记录这些更改。 此外,相关事实必须使用基于时间的查找来检索与事实日期相关的维度键值。 Power BI 语义模型使用 Power Query,因此无法生成此结果。 不过它可以从预加载的 SCD 类型 2 维度表加载数据。
提示
若要了解如何在 Fabric 仓库中实现类型 2 SCD 维度表,请参阅 “管理历史更改”。
Power BI 语义模型应支持查询成员的历史数据,而不考虑更改,以及成员版本,该版本表示成员的特定状态。 在 Adventure Works 的上下文中,此设计可以查询销售人员,而无需考虑分配的销售区域,或查询销售人员的特定版本。
为了实现此要求,Power BI 语义模型维度表必须包含用于筛选销售人员的列,以及用于筛选特定版本的销售人员的其他列。 版本列必须提供不明确的说明,例如 David Campbell (12/15/2008-06/26/2019)
或 David Campbell (06/27/2019-Current)
。 还必须让报表作者和使用者了解 SCD 类型 2 的基础知识,以及如何通过应用正确的筛选器来实现适当的报表设计。
最好是包含允许视觉对象向下钻取到版本级别的层次结构。
角色扮演维度
角色扮演维度是能以不同方式筛选相关事实的维度 。 例如,在 Adventure Works 中,日期维度表与经销商销售事实有三种关系。 可以使用同一维度表按订单日期、发货日期或交货日期筛选事实。
数据仓库中接受的设计方法是定义单个日期维度表。 在查询时,日期维度的“角色”是通过用于联接表的事实列建立的。 例如,在按订单日期分析销售情况时,表联接与经销商销售订单日期列相关。
在 Power BI 语义模型中,可以通过在两个表之间创建多个关系来模仿此设计。 在 Adventure Works 的示例中,数据和经销商销售表具有三种关系。
虽然此设计可行,但两个 Power BI 语义模型表之间只能有一个 活动 关系。 所有其他关系都必须设置为非活动状态。 具有单个活动关系意味着有从日期到经销商销售的默认筛选器传播。 在此实例中,活动关系设置为报表使用的最常见筛选器,在 Adventure Works 中是订单日期关系。
使用非活动关系的唯一方法是定义使用 USERELATIONSHIP 函数的 DAX 表达式。 在我们的示例中,模型开发者必须创建度量值以按发货日期和交货日期分析经销商销售情况。 这种工作可能比较繁琐,尤其是在经销商表定义了许多度量值的情况。 它还会创建一个杂乱无 章的数据 窗格,其中包含过度的度量值。 还有其他一些限制:
- 当报表作者依赖汇总列而不是定义度量值时,如果不编写报表级度量值,则它们无法实现非活动关系的汇总。 在 Power BI Desktop 中创作报表时,只能定义报表级别的度量值。
- 如果在日期与经销商销售之间只有一个活动的关系路径,则无法同时按不同类型的日期对经销商销售数据进行筛选。 例如,不能生成按已发货销售额绘制订单日期销售额的视觉对象。
为了克服这些限制,常见的 Power BI 建模技术是为每个角色扮演实例创建维度表。 可以使用 Power Query 创建每个维度表作为 引用查询 ,也可以使用 DAX 创建计算表 。 该模型可以包含一个表、一个Date
Ship Date
表和一个Delivery Date
表,每个表都与其各自的经销商销售表列具有一个活动关系。
此设计方法不要求为不同的日期角色定义多个度量值,并允许使用不同的日期角色同时进行筛选。 但是,使用此设计方法支付的一个小价格是,日期维度表将重复,从而导致模型存储大小增加。 由于维度表通常存储相对于事实数据表的行数较少,因此很少存在问题。
我们建议在为每个角色创建模型维度表时遵循良好的设计做法:
- 确保使用自述性列名。 虽然在所有日期表中都有一列(列名在其表中是唯一
Year
的),但默认情况下,它不是自描述的视觉对象标题。 请考虑重命名每个维度角色表中的列,以便表Ship Date
具有命名Ship Year
的年份列等。 - 如果相关,请确保表说明向报表作者(通过 数据 窗格工具提示)提供有关如何设置筛选器传播的反馈。 当模型包含一般命名表(例如
Date
,用于筛选许多事实数据表)时,这种清晰度非常重要。 例如,如果此表具有与经销商销售订单日期列的活动关系,请考虑提供如下Filters reseller sales by order date
表说明。
有关详细信息,请参阅活动与非活动关系指南。
杂项维度
如果有多个维度,特别是包含几个属性(可能只有一种)并且这些属性的值很少时,则杂项维度非常有用 。 好的候选项包括订单状态列,或客户人口统计列,如性别或年龄组。
垃圾维度的设计目标是将许多 小 维度合并到单个维度中,以减少模型存储大小,并通过显示更少的模型表来减少 数据 窗格混乱。
垃圾维度表通常是所有维度属性成员的笛卡尔积,具有代理键列来唯一标识每一行。 可以在数据仓库中构建维度,也可以通过使用 Power Query 创建执行完整外部查询联接的查询,然后添加代理键(索引列)。
将此查询作为维度表加载到模型。 还需要将此查询与事实查询合并,以便将索引列加载到模型,以支持创建“一对多”模型关系。
退化维度
退化维度是指筛选所需的事实数据表的属性。 Adventure Works 中有一个很好的例子,就是经销商销售订单号。 在此实例中,创建仅包含此列的独立表并不有意义,因为它会增加模型存储大小并导致 数据 窗格混乱。
在 Power BI 语义模型中,可以将销售订单号列添加到事实数据表,以允许按销售订单编号进行筛选或分组。 前引入的规则例外,即不应混合表类型(通常,模型表应为维度或事实)。
但是,如果 Adventure Works 经销商的销售表具有订单号和订单行号列,并且需要进行筛选,则创建退化维度表将是一个很好的设计。 有关详细信息,请参阅一对一关系指南(退化维度)。
无事实事实数据表
无事实事实数据表不包含任何度量值列 。 它仅包含维度键。
无事实事实数据表可以存储由维度键定义的观察值。 例如,在特定日期和时间,特定客户登录到您的网站。 可以定义一个度量值来计算无事实事实数据表的行,以对登录客户时间和数量进行分析。
更引人注目的使用事实数据表是存储维度之间的关系,它是一种 Power BI 语义模型设计方法,建议定义多对多维度关系。 在“多对多”维度关系设计中,无事实事实数据表称为桥接表。
例如,假设销售人员可以分配到一个或多个销售区域 。 桥接表将被设计为包含两列的无事实事实数据表:销售人员键和区域键。 这两列中可以存储重复的值。
这种“多对多”设计方法可以实现良好的文档记录,并且无需使用桥接表。 不过,当关联两个维度时,最佳做法还是使用桥接表方法。 有关详细信息,请参阅多对多关系指南(关联两个维度类型表)。
相关内容
有关星型架构设计或 Power BI 语义模型设计的详细信息,请参阅以下文章: