03.06 理解上下文转换
有两种方法进行上下文转换:一种是显式的,在计算列中使用CALCULATE实现上下文转换;另一种是隐式的,在计算列内部调用度量值而不使用CALCULATE。
计算列中的上下文转换
迭代函数
CALCULATE+SUM沿着关系方向传递筛选条件再聚合,类似sumif,但CALCULATE强大的地方在于可以和SUM, MIN, MAX, COUNTROWS等有聚合意义的函数一起使用。
度量值中的上下文转换在计算列内部调用度量值/在行上下文中调用度量值
上下文转换后的筛选器通常由所在表的列数决定的。如果表中有主键,则只筛选出一行;没有主键可能会筛选出相同行。
最后,需要注意上下文转换后的计值顺序。例如,CALCULATE执行上下文转换在先,应用内部筛选器在后。因此,CALCULATE的内部筛选器可以覆盖由上下文转换创建的筛选器
# 理解上下文转换
- 实现上下文转换的方法
- 直接输入,显式调用CALCULATE
- 隐式封装CALCULATE——度量值的特性
- 度量值自动添加的CALCULATE要求遵循书写规范,避免将度量值与计算列混淆
- 若要避免自动的上下文转换,可展开代码书写
- DAX的这些特性,是用来解决特定场景的需求的
- 筛选上下文和行上下文以不同方式在关系中运行:行上下文不会自动沿着关系方向传递,而筛选上下文从关系的“一”端传递到“多”端。因此,当发生上下文转换时,筛选上下文会自动传递到相关的表。
- 在销售表中,计算当前产品的销售,而不是计算整张表所有产品的销售
- 上下文转换后的可见行
- 模型中的表定义了主键:转换后的筛选上下文只包含一行,由主键唯一标识
- 模型中的表没有定义主键:上下文转换将在表的所有列上创建一个筛选器
- 如果表中所有行都不相同:筛选上下文唯一标识行
- 如果表中有相同的行:所有相同的行都将被包含在筛选上下文中
> 上下文转换是DAX计值过程中一个非常灵活的部分,灵活的同时意味着复杂,DAX的大部分复杂性都蕴含于此。上下文转换需要在理解CALCULATE函数的基础上学习。初识上下文转换
在理解CALCULATE的行为之后,你知道这个函数在计值过程中会执行一项非常重要的任务:将任何现有的行上下文转换为等效的筛选上下文。这就是我们说的上下文转换。为了演示该行为,我们创建一个包含CALCULATE表达式的计算列。由于计算列总是具有行上下文,因此会触发上下文转换。例如,在产品表中定义一个包含以下DAX表达式的计算列:Product[SumOfUnitPrice]=SUM(Product[Unit Price])有行上下文,没有筛选上下文公式对所有产品的标价求和。表达式在行上下文中计算,没有筛选上下文,因此它返回表中所有产品的单价之和,而不是正在计值的当前行产品的单价。你可以在下图中看到这种行为。SumOfUnitPrice在计算列中计值,返回所有产品单价的总和现在,你可以将表达式稍作修改创建一个新的计算列,加入CALCULATE:Product[SumOfUnitPriceCalc]=CALCULATE(SUM(Product[UnitPrice]))SUM计算列提供行上下文,CALCULATE将其转换为筛选上下文什么?只有一个参数的CALCULATE? 筛选器去哪儿了?实际上,我们用的是CALCULATE的极简形式。我们之前说过,CALCULATE惟一的必选参数是第一参数,因此在不使用任何筛选器的情况下调用CALCULATE是完全可以的。在这种情况下,CALCULATE不会使用其他条件更改现有的筛选上下文,它仍然执行你现在正在学习的行为:接受现有的行上下文(如果有的话),并将它们转换为等效的筛选上下文。请注意,所有现有的行上下文都合并到新的筛选上下文中,稍后我们会详细阐述。在本例中,CALCULATE查找现有的行上下文,并在产品表上发现一个由计算列定义的正在执行的行上下文。CALCULATE考虑这个行上下文,并用一个筛选上下文取而代之,该筛选上下文只包含行上下文正在迭代的当前行。我们将此行为称为上下文转换。一般来说,我们将以上过程简述为CALCULATE执行上下文转换,将所有行上下文合并到一个新的等效筛选上下文中。在CALCULATE内部,表达式SUM ( Product[Unit Price] )在只包含产品表当前行的筛选上下文中计值,由于CALCULATE执行了上下文转换。这一次的结果与产品单价(unit price)相同,如图所示。通过使用CALCULATE,行上下文被转换为筛选上下文,改变了结果当你第一次观察到这种行为,会发现很难理解为何CALCULATE要执行上下文转换。一旦开始使用之后你就一定会喜欢上该特性,因为多亏了它你才能创建强大的公式。此外,上下文转换还有另一个非常重要的作用。你可能还记得,筛选上下文和行上下文以不同方式在关系中运行:行上下文不会自动沿着关系方向传递,而筛选上下文从关系的“一”端传递到“多”端。因此,当发生上下文转换时,筛选上下文会自动传递到相关的表。通过在产品表定义以下两个新的计算列公式,你可以观察到这种行为:Product[SalesAmount]=SUM(Sales[SalesAmount])Product[SalesAmountCalc]=CALCULATE(SUM(Sales[SalesAmount]))由CALCULATE引发的上下文转换影响了对相关表的筛选如你所见,SalesAmount列包含所有销售额的总计,而SalesAmountCalc只包含当前产品的销售额。CALCULATE通过转换产品表的行上下文将筛选器传递到销售表,最终显示了当前产品的销售。在产品表新建列,使用CALCULATE将产品表的行上下文转换为筛选上下文,再传递到销售表请注意,当CALCULATE计算时,所有活动的行上下文都会发生上下文转换。实际上,在不同的表上可能有多个行上下文。例如,如果你在产品表创建计算列,使用AVERAGEX迭代客户表,那么有两个行上下文(产品和客户)将发生上下文转换,销售表将接收两个筛选器。考虑以下表达式:Product[SalesWithSUMX]=AVERAGEX(Customer,CALCULATE(SUM(Sales[SalesAmount])))CALCULATE内外的行上下文都将被转换为筛选上下文公式计算的是消费者购买该产品的平均花费(不是平均价格,而是总花费的平均值)。CALCULATE中的SUM函数在筛选上下文中计值,它只显示当前客户(由AVERAGEX迭代)和当前产品(由计算列迭代)的销售额。记住这个规则有一个简单的方法:在CALCULATE中没有行上下文,只存在一个筛选上下文。## 理解度量值中的上下文转换 理解上下文的转换非常重要,这是因为DAX还有另一个隐藏知识。到目前为止,我们一直使用函数和列来编写CALCULATE内部的表达式。但是,你还可以编写调用度量值的表达式。如果从计算列内部调用度量值会发生什么?更一般地说法是,如果从行上下文中调用度量值会发生什么?作为示例,你可以这样定义一个名为SumOfSalesAmount的度量值:[SumOfSalesAmount]:=SUM(Sales[SalesAmount])然后,你可以使用以下更简单的代码定义SalesWithSUMX计算列:Product[SalesWithSUMX]=SUMX(Customer,CALCULATE([SumOfSalesAmount]))### 自动添加的CALCULATE 使用CALCULATE表明公式发生了上下文转换,问题是,每当你从另一个表达式中调用已定义好的度量值时,DAX都会自动将度量值封装在CALCULATE中。因此,前面的表达式具有与以下表达式相同的行为:Product[SalesWithSUMX]=SUMX(Customer,[SumOfSalesAmount])这个公式没有显式调用CALCULATE,不过上下文转换依然在发生,因为DAX自动为度量值添加了CALCULATE。 这就是为什么编写代码时要区分列和度量值的原因,我们遵循的书写标准是避免将表名放在度量值的前面,但始终在列前面加上表名。实际上,在前面的公式中,在SumOfSalesAmount之前没有表名说明SumOfSalesAmount是一个度量值,因此,你知道发生了上下文转换。在嵌套中使用完整公式
上下文的自动转换使编写通过迭代执行复杂计算的公式变得容易。话虽如此,你仍然需要一些时间才能熟悉和使用这种技术。例如,如果你只想计算购买金额超过总体平均水平的客户的销售额总和,可以按如下方式编写度量值:[SalesMoreThanAverage]:=VAR AverageSales = AVERAGEX( Customer, [SumOfSalesAmount] )RETURN SUMX( Customer, IF( [SumOfSalesAmount]> AverageSales, [SumOfSalesAmount] ) )____AverageSales是被写死的变量。SumOfSalesAmount是度量值,封装了CALCULATE。在前面的代码中,我们使用SumOfSalesAmount作为在不同行上下文中计值的度量值。在定义变量时,我们使用它来计算客户销售额的平均值,而在SUMX的迭代中,我们使用它来检查当前客户的销售额与之前存储在变量中的平均值之间的关系。基于VAR的语法更易于阅读和维护(公式的计算也可能更快)。然而,本质在于理解不同语法背后不同的公式计值流,可以不用VAR,也无论你使用的是哪种DAX版本。如果没有真正理解和掌握这种上下文自动转换的机制,你可能花了大量时间阅读公式,但依然无法理解它的计算结果。在公式内部调用度量值时,上下文转换会自动发生,无法避免。这意味着在调用度量值时避免上下文转换的唯一方法是展开它的代码。例如,假设你用另一种方法编写了前面的代码。不使用变量,而是定义一个称为AverageSales的度量值表示客户的平均销售额,如下面的代码所示:[AverageSales]:= AVERAGEX( Customer, [SumOfSalesAmount] )
[SalesMoreThanAverage]:= SUMX( Customer, IF( [SumOfSalesAmount]>[AverageSales], [SumOfSalesAmount] ) )____AverageSales和SumOfSalesAmount是度量值,封装了CALCULATE。SUMX迭代每一行时,AverageSales和SumOfSalesAmount的行上下文是当前行,都被转换为筛选上下文,最终都是在当前行这一行中计算,所以AverageSales和SumOfSalesAmount始终相等,在IF中为FALSE,返回空值。在突出显示的行中,使用了[AverageSales]计算客户的平均销售额。问题是此时你正在迭代(SUMX)中调用度量值,这会使上下文转换发生。因此,[AverageSales]的结果将不是所有客户的平均销售额,而是你正在迭代的客户的平均销售额。因此,测试总是会失败,度量值返回一个空值,因为IF的真值分支永远不会执行。如果想避免上下文转换,你需要将调用的度量值写成完整形式:[SalesMoreThanAverage]:= SUMX( Customer, IF( [SumOfSalesAmount] >AVERAGEX( Customer, [SumOfSalesAmount] ), [SumOfSalesAmount] ) )使用完整形式后, SalesMoreThanAverage现在返回正确的结果。此外,值得注意的是,在这种情况下整个公式有两个嵌套的行上下文, 三个度量值调用。其中两个计算由SUMX迭代的当前客户的销售额, 另一个 (在AVERAGEX内部) 计算由AVERAGEX迭代的当前客户的销售额。理解这种特性之后你才能编写复杂的DAX代码来解决特定场景的需求。## 上下文转换之后究竟有多少可见行?
上下文转换是指将行上下文转换为等效的筛选上下文。这个说法需要进一步作些澄清。 行上下文总是包含单个行,而转换后CALCULATE创建的筛选上下文可能包含多个行,可能影响一列或多列,这取决于表结构。如果模型中的表定义了主键,那么CALCULATE就会创建一个只过滤主键的筛选上下文。实际上,这样的筛选上下文包含一行,由主键唯一标识。需要记住的是,主键可以通过使用表的元数据或创建将表作为目标的关系来定义。在这两种情况下,上下文转换都只筛选单个列,又由于主键列是表的唯一标识,因此只过滤单个行。如果表没有主键,那么上下文转换将在表的所有列上创建一个筛选器。这可能导致筛选器包含一个或多个行,具体取决于表内容。事实上,如果表中所有行都不同,那么筛选上下文可以惟一标识行。但是,如果表中有相同的行,那么所有相同行都将包含在筛选上下文中。在下面的示例中,Wrong Sales 和 Correct Sales返回不同的值:[Sales Amount]:=SUMX(Sales, Sales[Quantity]* Sales[Unit Price])
[Wrong Sales]:=SUMX(Sales,[Sales Amount])
[Correct Sales]:=SUMX(Sales, Sales[Quantity]* Sales[Unit Price])____Wrong Sales含有度量值,自动转换上下文,对计值内容筛选,由于表中有相同行,所以会重复计值。事实上,Wrong Sales遍历了销售表,对于每一行,它计算所有相同行的销售额,而Correct Sales计算每一行的销售额。因此,如果销售表中有多个相同的行,Wrong Sales将得到更高的值。在处理维度表时,这通常不是问题,因为维度中总是有一个主键。在这种情况下,唯一与当前行相同的行就是它自己。对于事实表或一般意义上没有主键的表,需要考虑可能存在重复的行;否则你可能会得到意想不到的结果。## 小结
- 上下文转换性能开销比较大。如果迭代具有10列和100万行的表并使用上下文转换,则CALCULATE需要应用10个筛选器,总共100万次。无论如何,这将是一个缓慢的操作。这并不是说应该避免依赖上下文转换。然而,它确实是CALCULATE的一个需要小心使用的特性。
- 上下文转换不仅过滤一行。存在于CALCULATE外部的原始行上下文始终标识唯一行,因为行上下文逐行迭代。当通过上下文转换将行上下文转换为筛选上下文时,新创建的筛选上下文将筛选具有相同值集的所有行。因此,您不应该假设上下文转换只创建了一个只有一行的筛选上下文,这一点非常重要,需要仔细体会。
- 上下文转换使用公式中不存在的列。尽管筛选器使用的这些列不可见,但它们仍然是表达式的一部分。这使得任何带有CALCULATE的公式都比最初看起来复杂得多。如果使用上下文转换,则表的所有列都是表达式的一部分,作为隐藏的筛选器参数,此行为可能会创建意外的依赖关系。
- 上下文转换从行上下文中创建筛选上下文。您可能还记得这段表述:“行上下文迭代表,而筛选上下文筛选整个模型”。一旦上下文转换将行上下文转换为筛选上下文,它将更改筛选器的性质,不再只迭代一行,而是筛选整个模型;关系成为表达式的一部分。换句话说,发生在一个表上的上下文转换可能会将其筛选效果传递到远离行上下文来源的其他表。
- 只要是存在行上下文的环境,上下文转换就会发生。例如,如果在计算列中使用CALCULATE,会发生上下文转换。计算列中有一个自动生成的行上下文,这足以使转换发生。
- 上下文转换所有的行上下文。当对多个表执行嵌套迭代时,上下文转换会考虑所有行上下文。它会使所有这些列无效,并为当前由所有活动行上下文迭代的所有列添加筛选器参数。
- 上下文转换使行上下文无效。虽然我们已经多次重复这个概念,但它值得再次引起您的注意。CALCULATE计算的表达式中没有任何有效的外部行上下文。所有外部行上下文都被转换为等效的筛选上下文。
理解上下文转换后的计值顺序
结合目前所学,相信你已经了解下面这两个在产品表中创建的计算列之间的区别:Product[SumOfUnitPrice]=CALCULATE(SUM(Product[Unit Price]))Product[SumOfAllUnitPrice]=CALCULATE(SUM(Product[Unit Price]),ALL(Product))它们都是计算列,并且都使用了CALCULATE,因此,两者都发生了上下文转换。SumOfUnitPrice应该只包含当前行的单价。然而,SumOfAllUnitPrice的值是多少?出于直觉,因为有ALL (Product),所以你很可能会期望它包含所有单价的总和。结果确实如此。然而,如果你遵循我们迄今所描述的规则,会发现这其中似乎还有一些问题。事实上,ALL (Product)返回整个产品表,有效地从筛选上下文删除了任何产品筛选器。然而,与此同时,上下文转换将筛选产品表并只显示一行。如果你把这两个条件取交集(AND),那么由于上下文转换产生的筛选器约束性更强,因此,它应该会胜出。那么,为什么结果是所有单价之和,而不是当前行的单价呢?原因在于通过上下文转换创建的筛选上下文和CALCULATE中编程式创建的筛选上下文之间有一个优先顺序。CALCULATE执行上下文转换在先,应用内部筛选器在后。因此,CALCULATE的内部筛选器可以覆盖由上下文转换创建的筛选器。
上下文转换易于使用但难以描述。事实上,上面的两个公式准确地计算出了我们想要的结果。然而,重要的是理解为什么会发生这种情况,核心在于,来自CALCULATE内部的筛选器覆盖了上下文转换而来的筛选器(换句话说,内部筛选器稍后应用),这句话是理解上下文转换中计值顺序的关键,计值顺序是上下文转换中最难理解且容易出错的知识点,后续我会追加更多案例详细介绍上下文转换并不是孤立的知识,需要掌握CALCULATE函数的计值过程才能正确理解,这里我假定你已经有一定的基础,通过下面三个案例,你可以测试一下自己对上下文转换的理解程度。### 案例一 xSUM是Table1的计算列,请思考在下图中它应该返回什么结果?公式是如何计值的?当前表为Table1### 案例二 xRANK是Table2的计算列,请思考在下图中它应该返回什么结果?公式是如何计值的?当前表为Table2### 案例文件下载(附结果) 下载链接### 案例三 [Test] 度量值的第9行MAX (‘销售明细’[出库日期] )是否被第3行VALUES (‘销售明细’[出库日期] )影响,为什么?Test :=MAXX( VALUES(‘销售明细’[出库日期]), CALCULATE( SUM(‘销售明细’[下单数量]), FILTER( ALL(‘销售明细’[出库日期]), ‘销售明细’[出库日期] =MAX(‘销售明细’[出库日期]) ) ))### 案例解析
案例一结果
xSum列每行都等于20。以表的第一行为例,公式计值过程如下:
- CALCULATE将当前行的行上下文转换为等价的筛选上下文,以第一行为例,这个筛选上下文是
- FILTER在初始的计值上下文环境中计算,不受步骤1生成的筛选上下文影响,得到的结果是Table1表A列等于a2的所有行
- 步骤2得到筛选器覆盖步骤1的筛选器,得到最终的筛选上下文
- CALCULATE第一参数在步骤3得到的筛选上下文中计值,结果为20
- 公式每行重复步骤1-4,完成计值 FILTER在初始的计值上下文环境是CALCULATE的外部环境,即计算列的当前行,只有行上下文(FILTER忽略行上下文),没有筛选上下文。步骤3的覆盖运算,可参考文章中的图解。#### 案例二结果 xRANK列从上至下是2,2,2,1,1,1。以表的第一行为例,公式计值过程如下:
- Rankx第二参数 CALCULATE(SUM(Table2[value])) 沿着表 ALL(Table2[value]) 每行计值,计值环境是调用RANKX的计算列的当前行上下文
和RANKX第一参数的行上下文(逐行迭代) - CALCULATE将这两种行上下文转换成筛选上下文,因为RANKX第一参数提供的行上下文位于内层,转换后会替换掉同一列(value列)的来自外部计算列的筛选条件,最终,沿着RANKX第一参数的每一行,CALCULATE的计值上下文和结果分别是:
沿着RANKX第一参数每行计值 - RANKX第二参数在调用RANKX的环境中再次计值,此时只有计算列提供的行上下文
,结果是1。注意我们目前讨论的范围始终限定为公式在Table2第一行的计值过程。 - 将步骤3得到的结果与步骤2中得到List值做比较,计算出最终排名2。
- 公式每行重复步骤1-4,完成计值
RANKX的计值流比较复杂,稍后会在RANKX公式篇专门介绍。但值得注意的是,公式的实际运行效率经过专门优化,非常快。
案例三结果
不影响。虽然还没有正式介绍CALCULATE计值流,通过前面两个示例,你应该已经了解到:CALCULATE的筛选器参数在调用CALCULATE的初始上下文环境中计值,对于本例而言,公式第九行的 MAX() 在调用CALCULATE的外部环境中计值,第三行的VALUES() 在此环境中提供行上下文,而MAX忽略行上下文,所以答案是不影响。相关推荐理解关系理解循环依赖理解变量计值上下文测试成员李星棋Follow this user#663问一个问题,文中的product表和sales表怎么获得呢?不懂为什么calculate后的SalesAmountCalc只有部分行有值,是因为sales表中有部分产品没有吗?赞0踩 回复6 天 之前成员李星棋Follow this user#664我大概知道了,高老师说的当前产品是指sales表中的产品,所以这样理解是可以的,我原来理解的当前产品是这个表文中的表里面看到的内容,所以才产生了这样的疑问。赞0踩 回复6 天 之前作者高飞Follow this user#667你开始的理解是对的,SalesAmountCalc为空的行是因为对应的产品在sales表没有销售数据赞0踩 回复6 天 之前成员Dot1xFollow this user#643xSUM=20,我是这样理解不知道对不对哈,请老师指点一下 1、cal中先计算第二参数2、Filter筛选出Table1,A列中的“a2”行3、Sum计算筛选出来的Value。赞0踩 回复8 天 之前作者高飞Follow this user#646xSUM是计算列,你是指每行都是20还是只有某一行?这个题主要考察的是对公式计值流的理解,你的描述忽略了一些重要细节,建议好好理解一下答案赞0踩 回复8 天 之前成员Dot1xFollow this user#651看了您的解析,明白了。赞0踩 回复7 天 之前成员Dot1xFollow this user#642请问一下,为啥值变成负数了,排名就变成这样了呢?![image.png])赞0踩 回复8 天 之前作者高飞Follow this user#645这是因为空值在计算排名的时候自动转换为0,在使用负数的时候才会发现这种变化。你可以参考数据类型和运算符这篇文章中关于隐式转换的描述赞0踩 回复8 天 之前成员muxiaomiFollow this user#631上下文和转换的概念,如果光看文字和图,可能对新人并不友好[可能我个人理解力比较差(划掉)],推荐可以先看CSG对这两节的讲解,然后再回头看书和文字,会有事半功倍的效果。Context Transition in DAX/Power BI: A Visual Guidehttps://youtu.be/XWBEHOKtgP4Row Context vs Filter Context in DAX: a Visual Guidehttps://youtu.be/qeS308beAAE赞0踩 回复11 天 之前作者高飞Follow this user#632挺好 可以搬运过来加个字幕赞0踩 回复11 天 之前成员Rainman1124Follow this user#606高老师:将步骤3得到的结果与步骤4中得到List值做比较,计算出最终排名2。是不是应该是:将步骤3得到的结果与步骤2中得到List值做比较,计算出最终排名2。赞0踩 回复15 天 之前作者高飞Follow this user#607多谢指出,已修改赞0踩 回复15 天 之前成员Rainman1124Follow this user#594案例1:1.计算列中,所以有行上下文;2.使用Calculate,所以行上下文会转换为筛选上下文;3.Calculate先算第2参数,再算第1参数,所以先看Filter结果;4.Calculate中无行上下文,虽然Filter为迭代函数,第2参数处于行上下文中,但是处于Calculate中,应该转化为筛选上下文;5.所以每行的计算列结果都为Calculat(Sum(Table1[Value]),Table1[A]=”a2″),应该为20。赞0踩 回复16 天 之前作者高飞Follow this user#597前3点没有问题,整体描述的过于简单,这里的关键是1.FILTER的计值环境 2. 行上下文转换而来的筛选上下文和FILTER生成的筛选上下文之间的相互关系,谁替换谁?赞0踩 回复15 天 之前游客Rainman1124Follow this user#600谢谢高老师!1.应该是先生成行下下文转换的筛选上下文,这个筛选上下文视为Filter的外部上下文,然后Filter生成的筛选上下文替换行上下文转换而来的筛选上下文。这样理解对吗?赞0踩 回复15 天 之前作者高飞Follow this user#601后半段描述是对的,但是前面不对,而且整个描述的逻辑前后是矛盾的,如果“这个筛选上下文视为Filter的外部上下文”,那FILTER会在这个环境中计值,后面的结果就错了。正确的理解参考我发布在文章中的案例解析把赞0踩 回复15 天 之前成员1366402Follow this user#587案例1的XSUM计算列的结果应该都是20,我理解的计算方式是:①计算SUM,计算列全部为总和100②calculate第一参数对计算列每一行转换并遍历相同行的总和,这里没有相同行,所以当前计算列的结果应该和value列结果一致③calculate第一参数外的条件为filter的结果,filter对表’table1’的每一行进行遍历,只取列名为A,交集行内容为”a2″的值④第三步的筛选条件应用到第二步的结果中赞0踩 回复16 天 之前作者高飞Follow this user#589结果是正确的,但是理解有偏差,这部分知识还需要熟悉CALCULATE计值流才能掌握,明天我会公布正确的计值过程,可以留意一下赞0踩 回复16 天 之前成员Rainman1124Follow this user#586高老师,我建立以下计算列,直接出错。 Product[SalesWithSUMX] = AVERAGEX ( Customer, CALCULATE ( SUM ( SalesSalesAmount… 阅读更多 »赞0踩 回复16 天 之前[作者高飞Follow this user#590不需要关系,因为这两个表之间没有相互筛选。报了什么错?赞0踩 回复16 天 之前成员Rainman1124Follow this user#591高老师,我前面用的是powerpivot in excel ,数据源为SQL Server,报错内容为:—出现错误(文件“xmvsquery.cpp”,第 13960 行,函数“XMVSXJoin::BuildRelationships”)。由于处理过程中遇到的错误数达到为操作定义的最大允许错误数,处理操作结束。已取消当前操作,因为事务中的另一个操作失败。—用PowerBI Desktop 是正常的。是不是和用excel有关?赞0踩 回复16 天 之前作者高飞Follow this user#592这是Excel的一个bug,你先把Excel更新到最新版本看看能否解决,参考《硬件配置和环境要求》赞0踩 回复16 天 之前成员Rainman1124Follow this user#593谢谢高老师!赞0踩 回复16 天 之前成员1366402Follow this user#585因为是初学,一直苦苦理解不了,目前感觉有那么一点点理解最最最简单的上下文了,理解范围也仅限于计算列,希望老师们指点;每一列是一个集合或一个表,一到多列组成一个表,每一列的每一个单元格与表中多列的行交集;新增计算列,计算列本身携带行上下文特性,新增的计算列每一行都是SUM函数对单价列进行遍历,我理解的类似for each对集合高斯累加;SUM套上calculate以后对第一参数进行重新设定筛选条件,这里只有第一参数,则代表没有筛选条件或者说筛选条件是当前自身行;赞0踩 回复16 天 之前作者高飞Follow this user#588表格模型的列和列是相互独立存储的,相比于行式数据库更容易做聚合运算,而且没有单元格的概念,都是对整列运算。聚合函数忽略行上下文,聚合整列,理解成遍历也可以加了calculate之后是有筛选条件的,是由行上下文转换而来的筛选上下文赞0踩 回复16 天 之前成员pleasureyuerFollow this user#580看完一遍一头雾水,明天继续第2遍,第3遍。。。如下两个公式,写出来了,理解不了,痛苦中。总是陷入自己的脑回路死循环。值1 超过均值的 和 v-wrong =var avg1 = AVERAGE(‘t1′[值1])return SUMX(FILTER(‘t1’,t1[值1]>avg1),SUM(t1[值1]))值1 超过均值的 和v-correct =var avg1 = AVERAGE(‘t1′[值1])return SUMX(FILTER(‘t1’,t1[值1]>avg1),CALCULATE(SUM(t1[值1])))赞0踩 回复18 天 之前作者高飞Follow this user#581v-wrong你可以这样思考,在数据视图中给t1新建计算列SUM(t1[值1]),然后筛选t1的任意一列,看计算列数据是否变化赞0踩 回复18 天 之前