Power BI Desktop 中的复合模型指南

本文面向开发 Power BI 复合模型的数据建模人员。 介绍了复合模型用例,并提供了设计指南。 具体而言,本指南可以帮助你确定复合模型是否适合你的解决方案。 如果适合,本文还将帮助你设计最佳复合模型和报表。

注意

本文不涵盖对复合模型的介绍。 如果你不完全熟悉复合模型,建议先阅读在 Power BI Desktop 中使用复合模型一文。

由于复合模型至少包含一个 DirectQuery 源,因此,必须深入了解模型关系DirectQuery 模型DirectQuery 模型设计指南

复合模型用例

根据定义,复合模型组合了多个源组。 源组可以表示导入的数据或与 DirectQuery 源的连接。 DirectQuery 源可以是关系数据库或其他表格模型,可以是 Power BI 语义模型(以前称为数据集)或 Analysis Services 表格模型。 当表格模型连接到另一个表格模型时,它称为“链接”。 有关详细信息,请参阅使用适用于 Power BI 语义模型和 Analysis Services 的 DirectQuery

注意

当模型连接到表格模型但未使用其他数据扩展它时,它不是复合模型。 在本例中,它是连接到远程模型的 DirectQuery 模型,因此它只包含一个源组。 可以创建这种类型的模型来修改源模型对象属性,例如表名、列排序顺序或格式字符串。

在扩展企业语义模型时(作为 Power BI 语义模型或 Analysis Services 模型时),连接到表格模型尤其相关。 企业语义模型是数据仓库开发和操作的基础。 它提供数据仓库中数据的抽象层,以呈现业务定义和术语。 它通常用作物理数据模型与报表工具(如 Power BI)之间的链接。 在大多数组织中,它由一个中心团队管理,这就是它被描述为企业的原因。 有关详细信息,请参阅企业 BI 使用情况方案。

可以考虑在以下情况下开发复合模型。

  • 模型可以是 DirectQuery 模型,并且需要提高性能。 在复合模型中,可以通过为每个表设置适当的存储来提高性能。 还可以添加用户定义的聚合。 本文稍后会介绍这两种优化。
  • 你希望将 DirectQuery 模型与必须导入到该模型中的更多数据组合在一起。 可以从其他数据源或计算表中加载导入的数据。
  • 需要将两个或多个 DirectQuery 数据源合并到单个模型中。 这些源可以是关系数据库或其他表格模型。

注意

复合模型不能包含与某些外部分析数据库的连接。 这些数据库包括 SAP Business Warehouse 和 SAP HANA(将 SAP HANA 视为多维源时)。

评估其他模型设计选项

虽然 Power BI 复合模型可以解决特定的设计挑战,但它们可能会导致性能降低。 此外,在某些情况下,可能会出现意外的计算结果(本文后面部分所述)。 出于这些原因,请评估其他模型设计选项(如果存在)。

最好尽可能在导入模式下开发模型。 此模式提供最大的设计灵活性和最佳性能。

但是,不能始终通过导入模型来解决与大型数据卷或近实时数据报告相关的问题。 在这两种情况下,都可以考虑使用 DirectQuery 模型,前提是数据存储在 DirectQuery 模式支持的单一数据源中。 有关详细信息,请参阅 Power BI Desktop 中的 DirectQuery 模型

提示

如果目标只是使用更多数据扩展现有表格模型,请尽可能将该数据添加到现有数据源。

表存储模式

在复合模型中,可以为每个表(计算表除外)设置存储模式

  • DirectQuery:建议为表示大数据卷的表或需要提供准实时结果的表设置此模式。 数据永远不会导入这些表中。 通常,这些表将是事实类型表,即汇总的表。
  • 导入:建议为不用于在 DirectQuery 或混合模式下对事实数据表进行筛选和分组的表设置此模式。 这也是基于 DirectQuery 模式不支持的源的表的唯一选项。 计算表始终为导入表。
  • 双重:建议为维度类型表设置此模式,在可能的情况下,将与来自同一源的 DirectQuery 事实类型表一起查询这些表。
  • 混合:如果你想要实时包含最新的数据更改,或者如果你想要通过导入分区提供对最常用数据的快速访问,同时将大量不常使用的数据保留在数据仓库中,建议你将导入分区以及一个 DirectQuery 分区添加到事实数据表来设置此模式。

Power BI 查询复合模型时,有几种可能的场景。

  • 仅查询导入或双重表:Power BI 从模型缓存中检索所有数据。 它将提供尽可能快的性能。 这种情况在筛选器或切片器视觉对象查询的维度类型表中很常见。
  • 查询同一源中的双重表或 DirectQuery 表:Power BI 通过向 DirectQuery 源发送一个或多个本机查询来检索所有数据。 它将提供良好的性能,尤其是在源表上存在适当的索引时。 这种情况在与双重维度类型表和 DirectQuery 事实类型表相关的查询中很常见。 这些是源组内查询,因此,所有一对一关系或一对多关系都会被评估为常规关系
  • 查询同一源中的双重表或混合表:这种方案是前两种方案的组合。 如果导入分区中提供了数据,则 Power BI 从模型缓存中检索数据,否则它将一个或多个本机查询发送到 DirectQuery 源。 它将提供尽可能快的性能,因为在数据仓库中仅查询一部分数据,尤其是在源表上存在适当的索引时。 对于双重维度类型表和 DirectQuery 事实类型表,这些是源组内查询,因此,所有一对一关系或一对多关系都会被评估为常规关系。
  • 所有其他查询:这些查询涉及跨源组关系。 这可能是因为导入表与 DirectQuery 表相关,或者双重表与不同源中的 DirectQuery 表相关,在这种情况下,其行为与导入表类似。 所有关系均评估为有限关系。 这也意味着,应用于非 DirectQuery 表的分组必须作为具体化子查询发送到 DirectQuery 源(虚拟表)。 在这种情况下,本机查询可能效率低下,尤其是对于大型分组集。

总之,我们建议:

  • 请仔细考虑一下,复合模型是正确的解决方案(虽然它允许不同数据源的模型级集成),但它还引入了可能具有各种后果的设计复杂性(本文后面部分所述)。
  • 当表是存储大型数据卷的事实类型表,或者当它需要提供近实时结果时,请将存储模式设置为“DirectQuery”。
  • 请考虑使用混合模式,方法是定义增量刷新策略和实时数据,或通过使用 TOMTMSL 或第三方工具对事实数据表进行分区。 有关详细信息,请参阅语义模型的增量刷新和实时数据高级数据模型管理使用方案。
  • 当表为维度类型表,并且将与同一源组中的 DirectQuery 或混合事实类型表一同查询时,请将存储模式设置为“双重”。
  • 设置适当的刷新频率,使双重表和混合表(以及任何从属的计算表)的模型缓存与源数据库保持同步。
  • 尽量确保源组(包括模型缓存)之间的数据完整性,因为有限关系将在相关列值不匹配时消除查询结果中的行。
  • 尽可能使用适当的索引优化 DirectQuery 数据源来实现高效联接、筛选和分组。

用户定义的聚合

可以将用户定义的聚合添加到 DirectQuery 表。 其目的是提高“更高粒度”查询的性能。

聚合缓存在模型中时,它们表现为导入表(尽管它们不能像模型表那样使用)。 将导入聚合添加到 DirectQuery 模型将导致复合模型。

注意

混合表不支持聚合,因为某些分区在导入模式下操作。 不能在单个 DirectQuery 分区级别添加聚合。

建议聚合遵循基本规则:其行计数应至少比基础表小 10 倍。 例如,如果基础表存储了十亿行,则聚合表不应超过一亿行。 此规则确保相对于创建和维护聚合的成本而言,有足够的性能增益。

跨源组关系

当模型关系跨越源组时,它被称为“跨源组关系”。 跨源组关系也是有限关系,因为没有保证的“一”端。 如需了解详细信息,请参阅关系评估

注意

在某些情况下,可以避免创建跨源组关系。 请参阅本文后面的使用同步切片器主题。

定义跨源组关系时,请考虑以下建议。

  • 使用低基数关系列:为了获得最佳性能,我们建议关系列为低基数,这意味着它们应存储少于 50,000 个唯一值。 在组合表格模型和非文本列时,此建议尤为适用。
  • 避免使用大型文本关系列:如果必须在关系中使用文本列,则通过将基数乘以文本列的平均长度来计算筛选器的预期文本长度。 可能的文本长度不应超过 1,000,000 个字符。
  • 提高关系粒度:如果可能,请以更高的粒度级别创建关系。 例如,与其在日期键上关联日期表,不如改用月份键。 此设计方法要求相关表包含一个月键列,并且报表无法显示每日事实数据。
  • 努力实现简单的关系设计:仅在需要时创建跨源组关系,并尝试限制关系路径中的表数。 此设计方法将有助于提高性能并避免不明确的关系路径

警告

由于 Power BI Desktop 不会彻底验证跨源组关系,因此可能会创建不明确的关系。

跨源组关系方案 1

考虑复杂关系设计的方案,以及它如何产生不同但有效的结果。

在此方案中,源组 A 中的 Region 表与源组 B 中的 Date 表和 Sales 表有关系。Region 表与 Date 表之间的关系处于活动状态,而 Region 表与 Sales 表之间的关系处于非活动状态。 此外,Region 表和 Sales 表之间存在活动关系,这两者都在源组 B 中。Sales 表包含一个名为 TotalSales 的度量值,Region 表包含两个名为 RegionalSales 和 RegionalSalesDirect 的度量值。

Diagram shows the scenario 1 model design as described in the previous paragraph.

下面是度量值定义。

TotalSales = SUM(Sales[Sales])
RegionalSales = CALCULATE([TotalSales], USERELATIONSHIP(Region[RegionID], Sales[RegionID]))
RegionalSalesDirect = CALCULATE(SUM(Sales[Sales]), USERELATIONSHIP(Region[RegionID], Sales[RegionID]))

请注意 RegionalSales 度量值如何引用 TotalSales 度量值,而 RegionalSalesDirect 度量值不引用。 相反,RegionalSalesDirect 度量值使用表达式 SUM(Sales[Sales]),它是 TotalSales 度量值的表达式。

结果的差异非常细微。 当 Power BI 评估 RegionalSales 度量值时,它会将 Region 表中的筛选器同时应用于 Sales 表和 Date 表。 因此,筛选器还会从 Date 表传播到 Sales 表。 相比之下,当 Power BI 评估 RegionalSalesDirect 度量值时,它只会将筛选器从 Region 表传播到 Sales 表。 RegionalSales 度量值和 RegionalSalesDirect 度量值返回的结果可能不同,即使表达式在语义上是等效的。

重要

每当将 CALCULATE 函数与作为远程源组中度量值的表达式一起使用时,请全面测试计算结果。

跨源组关系方案 2

假设跨源组关系具有高基数关系列。

在此方案中,Date 表与 Sales 表在 DateKey 列上相关。 DateKey 列的数据类型为整数,存储使用 yyyymmdd 格式的整数。 这些表属于不同的源组。 此外,这是一种高基数关系,因为 Date 表中的最早日期是 1900 年 1 月 1 日,最晚日期是 2100 年 12 月 31 日,因此表中总共有 73,414 行(1900-2100 时间跨度内的每个日期一行)。

Diagram shows the scenario 2 model design as described in the previous paragraph.

有两种情况值得关注。

首先,使用 Date 表列作为筛选器时,筛选器传播将筛选 Sales 表的 DateKey 列以评估度量值。 按单个年份(如 2022 年)进行筛选时,DAX 查询将包含一个筛选表达式,例如 Sales[DateKey] IN { 20220101, 20220102, …20221231 }。 当筛选器表达式中的值数较大或筛选器值为长字符串时,查询的文本大小可能会变得非常大。 Power BI 生成长查询和数据源运行查询的成本很高。

其次,使用 Date 表列(如 Year、Quarter 或 Month)作为分组列时,会生成包含年、季度或月的所有唯一组合以及 DateKey 列值的筛选器。 查询的字符串大小(包含分组列和关系列的筛选器)可能会变得非常大。 当分组列数和/或联接列(DateKey 列)的基数很大时,尤其如此。

若要解决任何性能问题,可以:

  • 将 Date 表添加到数据源,从而导致单个源组模型(这意味着它不再是复合模型)。
  • 提高关系的粒度。 例如,可以将 MonthKey 列添加到这两个表中,并在这些列上创建关系。 但是,通过提高关系的粒度,你将失去报告每日销售活动的能力(除非使用 Sales 表中的 DateKey 列)。

跨源组关系方案 3

假设跨源组关系中的表之间没有匹配值。

在此方案中,源组 B 中的 Date 表与该源组中的 Sales 表以及源组 A 中的 Target 表有关系。与 Year 列相关的 Date 表中的所有关系都是一对多关系。 Sales 表包含存储销售额的 SalesAmount 列,而 Target 表包含存储目标金额的 TargetAmount 列。

Diagram shows the scenario 3 model design as described in the previous paragraph.

Date 表存储 2021 年和 2022 年。 Sales 表存储 2021 年 (100) 和 2022 年 (200) 的销售额,而 Target 表存储 2021 年 (100)、2022 年 (200) 和 2023 年 (300)(未来一年)的目标金额。

Diagram shows the scenario 3 table data as described in the previous paragraph.

当 Power BI 表视觉对象通过对 Date 表中的 Year 列进行分组并对 SalesAmount 和 TargetAmount 列求和来查询复合模型时,它不会显示 2023 年的目标金额。 这是因为跨源组关系是一种有限的关系,因此它使用 INNER JOIN 语义,这消除了两端没有匹配值的行。 但是,它将生成正确的总目标金额 (600),因为 Date 表筛选器不适用于其计算。

Diagram shows a table visual that doesn't show the 2023 target amount. Also, the target amount total of 600 doesn't equal the two shown values for 2021 and 2022 (100 and 200).

如果 Date 表与 Target 表之间的关系是源组内部关系(假设 Target 表属于源组 B),则视觉对象将包含一个(空白)年,以显示 2023 年(和任何其他不匹配年份)的目标金额。

重要

若要避免错误报告,请确保当维度表和事实数据表位于不同的源组中时,关系列中存在匹配的值。

有关有限关系的详细信息,请参阅关系评估

计算

将计算列和计算组添加到复合模型时,应考虑特定的限制。

计算列

添加到从关系数据库(如 Microsoft SQL Server)中获取数据的 DirectQuery 表的计算列,仅限于一次对单个行进行操作的表达式。 这些表达式不能使用 DAX 迭代器函数(如 SUMX)或筛选器上下文修改函数(如 CALCULATE)。

注意

无法添加依赖于链式表格模型的计算列或计算表。

远程 DirectQuery 表上的计算列表达式仅限于行内计算。 但是,可以创作此类表达式,但在视觉对象中使用时会导致错误。 例如,如果使用表达式 [Product Sales] / SUM (DimProduct[ProductSales]) 将计算列添加到名为 DimProduct 的远程 DirectQuery 表,则可以成功将表达式保存在模型中。 但是,在视觉对象中使用时会导致错误,因为它违反了行内评估限制。

相比之下,添加到远程 DirectQuery 表的计算列(该表是一个表格模型,可以是 Power BI 语义模型,也可以是 Analysis Services 模型)更加灵活。 在这种情况下,允许所有 DAX 函数,因为将在源表格模型中计算表达式。

许多表达式要求 Power BI 将计算列具体化,然后再将其用作组或筛选器,或将其聚合。 在大型表中具体化计算列时,根据计算列所依赖的列的基数,CPU 和内存的成本可能很高。 在这种情况下,建议将这些计算列添加到源模型。

注意

将计算列添加到复合模型时,请务必测试所有模型计算。 上游计算可能无法正常工作,因为它们没有考虑它们对筛选器上下文的影响。

计算组

如果计算组存在于连接到 Power BI 语义模型或 Analysis Services 模型的源组中,则 Power BI 可能会返回意外结果。 有关详细信息,请参阅计算组、查询和度量值评估

模型设计

应始终通过采用星型架构设计来优化 Power BI 模型。

提示

有关详细信息,请参阅了解星型架构和 Power BI 的重要性

请务必创建独立于事实数据表的维度表,以便 Power BI 可以正确解释联接并生成高效的查询计划。 虽然本指南适用于任何 Power BI 模型,但尤其适用于你识别将成为复合模型的源组的模型。 它将允许在下游模型中更简单、更高效地集成其他表。

尽可能避免在一个源组中使用与不同源组中的事实数据表相关的维度表。 这是因为具有源组内部关系比跨源组关系更好,尤其是对于高基数关系列。 如前所述,跨源组关系依赖于在关系列中具有匹配的值,否则可能会在报表视觉对象中显示意外结果。

行级别安全性

如果模型包括用户定义的聚合、导入表上的计算列或计算表,请确保正确设置并测试任何行级安全 (RLS)。

如果复合模型连接到其他表格模型,则 RLS 规则仅应用于定义它们的源组(本地模型)。 它们不会应用于其他源组(远程模型)。 此外,不能在来自另一个源组的表上定义 RLS 规则,也不能在与另一个源组有关系的本地表上定义 RLS 规则。

报表设计

在某些情况下,可以通过设计优化的报表布局来提高复合模型的性能。

单个源组视觉对象

尽可能创建使用单个源组中的字段的视觉对象。 这是因为,当从单个源组检索结果时,视觉对象生成的查询性能会更好。 请考虑创建并排放置的两个视觉对象,用于从两个不同的源组检索数据。

使用同步切片器

在某些情况下,可以设置同步切片器以避免在模型中创建跨源组关系。 它允许你直观地合并源组,从而可以更好地执行。

假设模型有两个源组。 每个源组都有一个产品维度表,用于筛选经销商和 Internet 销售。

在此方案中,源组 A 包含与 ResellerSales 表相关的 Product 表。 源组 B 包含与 InternetSales 表相关的 Product2 表。 没有任何跨源组关系。

Diagram shows the model design as described in the previous paragraph.

在报表中,添加一个切片器,该切片器使用 Product 表的 Color 列筛选页面。 默认情况下,切片器会筛选 ResellerSales 表,但不筛选 InternetSales 表。 然后,使用 Product2 表的 Color 列添加隐藏的切片器。 通过设置相同的组名(在同步切片器“高级选项”中找到),应用于可见切片器的筛选器将自动传播到隐藏的切片器。

注意

虽然使用同步切片器可以避免创建跨源组关系,但会增加模型设计的复杂性。 请务必让其他用户了解你设计具有重复维度表的模型的原因。 通过隐藏不希望其他用户使用的维度表来避免混淆。 还可以向隐藏表添加说明文本,以记录其用途。

有关详细信息,请参阅同步单独的切片器

其他指南

下面是一些其他指南,可帮助你设计和维护复合模型。

  • 性能和规模:如果报表以前实时连接到 Power BI 语义模型或 Analysis Services 模型,则 Power BI 服务可以在报表之间重复使用视觉对象缓存。 将实时连接转换为创建本地 DirectQuery 模型后,报表将不再受益于这些缓存。 因此,可能会遇到性能降低甚至刷新失败的情况。 此外,Power BI 服务的工作负荷也会增加,这可能需要纵向扩展容量或在其他容量之间分配工作负荷。 有关数据刷新和缓存的详细信息,请参阅 Power BI 中的数据刷新
  • 重命名:不建议重命名复合模型使用的语义模型,也不建议重命名其工作区。 这是因为复合模型通过使用工作区和语义模型名称(而不是其内部唯一标识符)连接到 Power BI 语义模型。 重命名语义模型或工作区可能会中断复合模型使用的连接。
  • 治理:不建议将单一事实版本模型作为复合模型。 这是因为它将依赖于其他数据源或模型,如果更新这些数据源或模型,可能会导致复合模型中断。 而是建议将企业语义模型发布为单一事实版本。 将此模型视为可靠的基础。 然后,其他数据建模者可以创建复合模型,用于扩展基础模型以创建专用模型。
  • 数据世系:在发布复合模型更改之前,请使用数据世系语义模型影响分析功能。 这些功能在 Power BI 服务中提供,它们可以帮助你了解语义模型的相关性和使用方式。 务必了解,你无法对显示在世系视图中但实际位于另一工作区的外部语义模型执行影响分析。 若要对外部语义模型执行影响分析,你需要导航到源工作区。
  • 架构更新:在对上游数据源进行架构更改时,应在 Power BI Desktop 中刷新复合模型。 然后,需要将模型重新发布到 Power BI 服务。 请务必全面测试计算和依赖报表。

有关本文的详细信息,请查看以下资源。