4.5 处理多个表

处理多个表

既然您已经了解了评估上下文的基础知识,我们就可以描述上下文在关系方面的行为。实际上,很少有数据模型仅包含一个表。最有可能存在通过关系链接的几个表。如果SalesProduct之间存在关系,Product上的筛选上下文是否也筛选SalesSales表上的筛选呢?它筛选Product吗?因为有两种类型的评估上下文(行上下文和筛选上下文),并且关系具有两个方面(一侧和多侧),所以要分析四种不同的情况。

这些问题的答案已存在于本章学到的那句口头禅里了:“筛选上下文筛选;行上下文会迭代”,其效果是,“筛选上下文不会迭代;行上下文不会筛选。”

为了检查场景,我们使用包含六个表的数据模型,如图4-17所示。

图4-17 用于学习上下文和关系之间的交互的数据模型

该模型有几个值得注意的细节:

  • Sales到达Product Category,通过ProductProduct Subcategory,存在一系列的关系。
  • 唯一的双向关系是SalesProduct之间的关系。所有其余关系都设置为单个交叉筛选方向。

在下一部分中查看评估上下文和关系的详细信息时,该模型将非常有用。

行上下文和关系

行上下文进行迭代;它不会筛选。迭代是逐行扫描表并同时执行操作的过程。通常,人们想要某种汇总,例如总和或平均值。在迭代过程中,行上下文正在迭代单个表,它为表的所有列(仅该表)提供值。其他表尽管与迭代表有关,但在它们上没有行上下文。换句话说,行上下文不会自动与关系交互。

Sales表中的一个计算列为例,该列包含实体表中存储的单价与Product表中存储的单价之差。以下DAX代码不起作用,因为它使用Product [UnitPrice]列,并且Product上没有行上下文:

Sales[UnitPriceVariance] = Sales[Unit Price] - 'Product'[Unit Price]

作为计算列,在包含该列的表(即Sales表)上DAX自动生成行上下文。Sales上的行上下文使用Sales中的列值对表达式逐行评估。即使ProductSales处于一对多关系的“一”侧,该迭代仅发生在Sales表上。

当我们在关系的“多”方进行迭代时,我们可以在关系的一侧访问列,但是必须使用RELATED函数。RELATED接受列引用列作为参数,并检索目标表中相应行中的列值。RELATED只能引用一列,并且需要多个RELATED函数才能在关系的一侧访问多个列。以前的代码的正确版本如下:

Sales[UnitPriceVariance] = Sales[Unit Price] - RELATED ( 'Product'[Unit Price] )

RELATED要求在关系的“多”边表上具有行上下文(即,迭代)。如果行上下文在关系的“一”侧处于活动状态,则RELATED将不再有用,因为RELATED将通过该关系找到多个行。在这种情况下,也就是说,当在关系的一侧迭代时,要使用的函数是RELATEDTABLERELATEDTABLE返回与当前迭代表相关的多侧表的所有行。例如,如果要计算每种产品的销售数量,则将以下公式定义为Product上的计算列即可解决该问题:

Product[NumberOfSales] =
VAR SalesOfCurrentProduct = RELATEDTABLE ( Sales )
RETURN
    COUNTROWS ( SalesOfCurrentProduct )

此表达式计算Sales表中与当前产品相对应的行数。结果在图4-18中可见。

图4-18 RELATEDTABLE在关系的一侧的行上下文中很有用

RELATEDRELATEDTABLE都可以遍历关系链。它们不限于单跳。例如,可以使用与以前相同的代码创建一列,但是这次,在Product Category表中:

'Product Category'[NumberOfSales] =
VAR SalesOfCurrentProductCategory = RELATEDTABLE ( Sales )
RETURN
    COUNTROWS ( SalesOfCurrentProductCategory )

结果是类别的销售数量,该数量遍历从“产品类别”到“产品子类别”,再到Product之间的关系链,最终到达Sales表。 以类似的方式,可以在Product表中创建一个计算列,该列从Product Category表中复制类别名称。

'Product'[Category] = RELATED ( 'Product Category'[Category] )

在这种情况下,单个RELATED函数会遍历从产品到产品子类别到产品类别的关系链。

注意
RELATEDRELATEDTABLE通用规则的唯一例外是一对一关系。如果两个表共享一对一关系,则RELATEDRELATEDTABLE在两个表中都起作用,并且根据使用的函数,它们将得出一个列值或具有单行的表。

关于关系链,所有关系都必须属于同一类型,即一对多或多对一。如果链通过一对多关系将两个表链接到一个桥接表,然后通过多对一关系链接到第二个表,则RELATEDRELATEDTABLE均不适用于单向筛选传播。如后面所述,只有RELATEDTABLE可以使用双向筛选。另一方面,一对一关系同时表现为一对多关系和多对一关系。因此,一对多(或多对一)链中可以存在一对一关系而不会中断该链。

例如,在我们选择作为参考的模型中,客户与销售有关,而销售与产品有关。客户与销售之间存在一对多的关系,然后销售与产品之间存在多对一的关系。因此,关系链将客户与产品联系起来。但是,两个关系的方向不同。这种情况称为多对多关系。客户与购买的许多产品有关,而产品又与购买该产品的许多客户有关。我们将在第15章“高级关系”中介绍多对多关系。现在,让我们关注行上下文。如果一个人通过多对多关系使用RELATEDTABLE,那么结果将是错误的。考虑具有以下公式的 Product 中的计算列:

Product[NumOfBuyingCustomers] =
VAR CustomersOfCurrentProduct = RELATEDTABLE ( Customer )
RETURN
    COUNTROWS ( CustomersOfCurrentProduct )

先前代码的结果不是购买该产品的客户数量了。相反,结果是客户总数,如图4-19所示。

图4-19 RELATEDTABLE不适用于多对多关系

RELATEDTABLE无法遵循关系链,因为它们的方向不同。产品的行上下文未到达客户。值得注意的是,如果我们以相反的方向尝试公式,即如果我们计算每个客户购买的产品数量,则结果是正确的:每行代表客户购买的产品数量的数字不同。此行为的原因不是行上下文的传播,而是RELATEDTABLE生成的上下文转换。我们添加了此最终注释以进行全面披露。现在还没有时间对此进行详细说明。阅读第5章后,您将对此有更好的理解。

筛选上下文和关系

在上一节中,您了解了行上下文是迭代的,因此,它不使用关系。另一方面,筛选上下文筛选。筛选上下文不会应用于单个表。相反,它始终适用于整个模型。在这一点上,您可以将评估上下文规范更新为完整的表述:

筛选上下文筛选模型;行上下文迭代一个表。

因为筛选上下文筛选模型,所以它使用关系。筛选上下文自动与关系交互,并且其行为取决于关系的交叉筛选方向的设置方式。交叉筛选的方向在关系的中间用小箭头表示,如图4-20所示。

图4-20 筛选上下文和关系的行为

筛选上下文通过沿箭头允许的方向使用关系。在所有关系中,箭头允许从一侧到多侧传播,而当交叉筛选方向为BOTH时,也允许从一侧到多侧传播。

具有单向交叉筛选的关系是单向关系,而具有双向交叉筛选的关系都是双向关系。

此行为是直观的。尽管我们没有尽快对此进行解释,但是到目前为止,我们使用的所有报告都依赖于此行为。确实,在典型的报告中,通过Product [Color]进行筛选并汇总Sales [Quantity],人们会期望把Product的筛选传播到Sales。这正是发生的情况:产品处于关系的一侧;因此,无论交叉筛选的方向如何,Product上的筛选都会传播到Sales

因为我们的样本数据模型同时包含双向关系和许多单向关系,所以我们可以通过使用三种不同的度量值来演示筛选行为,这些度量值对三个表中的行数进行计数:SalesProductCustomer

[NumOfSales]:= COUNTROWS(销售)
[NumOfProducts]:= COUNTROWS(产品)
[NumOfCustomers]:= COUNTROWS(客户)

该报告在行上包含Product [Color]。因此,在筛选产品颜色的筛选上下文中评估每个单元格。结果如图4-21所示。

图4-21 显示了筛选上下文和关系的行为

在第一个示例中,筛选始终从关系的一侧传播到多侧。筛选从Product [Color]开始。从那里到达Sales表,ProductSales关系的多侧。对于Product表,本身就是同一张表。另一方面,NumOfCustomers始终显示相同的值,即客户总数。这是因为客户和销售之间的关系不允许从销售传播到客户。筛选从Product移动到Sales,但是从那里没有到达Customers

您可能已经注意到,SalesProduct之间的关系是双向关系。因此,Customer上的筛选器上下文也可以筛选SalesProduct。我们可以通过更改报告,按Customer[Education]而非Product[Color]进行切片来证明这一点。结果如图4-22所示。

4-22 通过Customer[Education]进行筛选,Product表也被筛选

这次,筛选从Customer启动。它可以到达Sales表,因为Sales在关系的多方面。此外,它从Sales传播到Product,因为SalesProduct之间的关系是双向关系,交叉筛选的方向是BOTH。

请注意,链中的单个双向关系不会使整个链成为双向的。实际上,一种类似的计算子类别数量的方法(例如下一项)表明,从Customer开始的筛选上下文未达到Product Subcategory

NumOfSubcategories := COUNTROWS ( 'Product Subcategory' )

将度量值添加到上一个报告中将产生如图4-23所示的结果,其中所有行的NumOfSubcategories均相同。

DAX4-23.jpg

图4-23 如果关系是单向的,则Customers无法筛选Product Subcategory

因为产品和产品子类别之间的关系是单向的,所以筛选不会传播到产品子类别。如果我们更新关系,将交叉筛选的方向设置为BOTH,结果将有所不同,如图4-24所示。

图4-24 如果关系是双向的,则*Customers*也可筛选*Product Subcategory*

对于行上下文,我们使用RELATEDRELATEDTABLE通过关系传播行上下文。另一方面,对于筛选上下文,不需要任何功能来传播筛选。筛选上下文筛选模型,而不是表。这样一来,一旦应用了筛选上下文,整个模型就会根据这些关系接受筛选的处理。

重要
从这些示例来看,对所有关系启用双向筛选似乎是使筛选器上下文传播到整个模型的一个不错的选择。绝对不是这样。稍后,我们将在第15章中深入介绍高级关系。双向筛选的复杂性比我们在本介绍性章节中分享的复杂得多,除非您对后果有一个清晰的认识,否则不要使用它们。通常,仅在严格要求时,才应使用CROSSFILTER函数以特定方式启用双向筛选。

你可能感兴趣的:(4.5 处理多个表)