DAX裸和与CALCULATE中包装的总和之间的差

时间:2019-08-31 14:19:06

标签: powerbi dax

SUM和包裹在CALCULATE中的总和有什么区别?

Measure1 = SUM( tab[col1]) )
Measure2 = CALCULATE ( SUM( tab[col1]) ) )

更新。

我给了另一个answer问题,它使用包装的计算。问题的作者对此做了解释,但我仍然不明白。这是作者建议的措施:

Expected Result =
SUMX (
    VALUES ( Unique_Manager[Manager] ),
    VAR SumBrand = CALCULATE ( SUM ( Budget_Brand[BudgetBrand] ) )
    VAR SumProduct = CALCULATE ( SUM ( Budget_Product[BudgetProduct] ) )
    RETURN
        IF ( ISBLANK ( SumProduct ), SumBrand, SumProduct )
)

解释:

  

(...)请注意,我将总和打包在CALCULATE中。这样做是为了   从SUMX内部的行上下文执行上下文转换(   特定的Manager),以将该Manager作为过滤器上下文   BudgetBrand和BudgetProduct。将这些总和存储为变量使得   对于更易读的IF行,仅要求计算SumProduct   一次而不是两次。

我知道什么是过滤器上下文。但是我不明白什么是上下文过渡。还有其他转换吗?

3 个答案:

答案 0 :(得分:3)

在行上下文中使用CALCULATE时,将发生过渡,其中每一行成为过滤器,并使用该行的所有列作为过滤器为每一行扫描整个表。如果表重复,这将影响性能,并且还可能导致意外结果。

您提供的两个版本可能返回相同的值。但是,如果不这样做,可能是因为表中的行重复。

您可能会发现this article非常有用。

答案 1 :(得分:2)

此答案通常解决CALCULATE的使用问题,并涉及过滤器上下文,行上下文和上下文转换的主题。

根据原始问题的定义,根据下面的原始响应,[Measure1]和[Measure2]的行为相同。有关对CALCULATE的更详细说明,请参见对此答案的修改。

通常,如果要在行上下文中查看表达式,则将CALCULATE与单个参数一起使用以引起上下文转换(行上下文->过滤上下文)。

我看到的一个常见的初学者错误是表达式中的多余/不必要的CALCULATECALCULATE仅在要影响第一个参数的过滤器上下文时才应使用。这有两种一般形式:

  1. 您要使用参数2-N添加,删除或更改过滤器上下文。
  2. 您要转换行上下文以过滤上下文。

两者可能会聚在一起。

以上所述的推论是,除非调用站点位于行上下文中,否则切勿将CALCULATE与一个arg一起使用。

修改:基于评论和更新的问题

这个问题似乎有些混乱。因此,在进入上下文转换之前,我将首先进行清理。

注意:无论我在下面提到的CALCULATE还是什么,您都可以阅读CALCULATETABLE,其行为几乎相同。第一个用于标量表达式,第二个用于表表达式。

度量不是仅仅是命名的DAX表达式。量度是一个命名的DAX表达式,其周围有一个隐含的CALCULATE 。因此,如果要用量度表达式替换对量度的引用,则不只是做简单的文本替换,还可以编写CALCULATE ( <measure body> )

如果要询问的形式是自洽的,我尽量不要猜测一个问题的不同含义。我相信你想问:

  

以下DAX表达式之间有什么区别?

     

SUM ( 'tab'[col1] )

     

     

CALCULATE ( SUM ( 'tab'[col1] ) )

这与您提出的问题本质上不同,因为您要询问的是两个完全定义的度量,而不是两个DAX代码段。这些措施的行为相同,因为它们的扩展在逻辑上是等效的:

//Measure 1 definition:
Measure1 = SUM ( 'tab'[col1] )

// Measure 1 expands to the following, and you would use this if you were
// replacing a reference with code:
//Expansion1:
CALCULATE ( SUM ( 'tab'[col1] ) )
//Measure2 definition and expansion:
Measure2 = CALCULATE ( SUM ( 'tab'[col1] ) )

//Expansion2:
CALCULATE ( CALCULATE ( SUM ( 'tab'[col1] ) ) )

因此,您的度量在语义上(尽管不是文本上)是相同的。两者都以包裹在SUM中的CALCULATE的形式执行。 [Measure2]刚好在扩展时有一个额外的CALCULATE

那么CALCULATE的作用是什么?很多。作为参考,当我进行DAX培训时,CALCULATE以及过滤器和行上下文是一个数小时的时段。

CALCULATE执行以下操作。

  1. 执行上下文转换。它创建一个新的过滤器上下文,在其中可以评估其第一个参数表达式。这个新的过滤器上下文包括以下内容(合并为一个过滤器上下文):

    a。 CALCULATE

    的调用站点上存在任何外部过滤器上下文

    b。 CALCULATE

  2. 的呼叫站点上存在任何行上下文
  3. 计算参数2-N(称为setfilters)以修改(添加,删除或修改现有的)步骤(1)中的过滤器上下文,最后

  4. 在由步骤(1)和(2)确定的新过滤器上下文中评估arg1中的表达式。

所以,这有几个问题,即:

  1. 什么是过滤器上下文?
  2. 什么是行上下文?
  3. 将行上下文转换为过滤器上下文是什么意思?

因此,首先,过滤上下文。筛选器上下文来自多个DAX函数,包括CALCULATECALCULATETABLESUMMARIZESUMMARIZECOLUMNSGROUPBY。此列表并非旨在详尽无遗,但确实涵盖了一些非常常见的功能。

只要您在报表工具中与表格模型互动,例如Excel数据透视表或Power BI报表,您在GUI中的操作会生成查询,这些查询可用于填充任何可视化对象。从这些(和其他)报告工具的角度来看,过滤器上下文来自:

  • 行/列/轴标签(不要混淆数据透视表行以提供行上下文-不会)
  • 数据透视表过滤器
  • 切片器
  • 其他视觉效果的选择作为交叉过滤
  • 视觉/页面/报告/追溯/工具提示过滤器

您可以将过滤器上下文视为一组“表” [Column]->值映射。无论选择什么文字值或满足选择条件的文字值,都会成为过滤器上下文。

例如,让我们考虑一个矩阵视觉,在行上具有“日历” [Year],在列上具有“ Calendar” [MonthName],“ Product” [Category] ​​=“ Clothing”的切片器以及页面级过滤器为“日历” [年份]> 2015。我们将查看在第三行第四列的​​矩阵中评估的度量[M]的过滤器上下文(2018年4月)

Filter Context:
'Calendar'[Year]=2018
'Calendar'[Year]>2015
    => 'Calendar'[Year] IN {2016, 2017, 2018, ..., N} // for whatever years exist in the calendar
'Calendar'[Month]="April"
'Product'[Category]="Clothing"

基于年和月的交集,矩阵的每个单元格都有其自己的过滤器上下文,但其余部分将保持不变。对于底部的总计行,过滤器上下文将没有来自矩阵的特定年份,但仍会受到页面级过滤器的影响。对于右侧的总计列,将没有月份的上下文,但是会有特定的年份。对于矩阵右下角的Grant total单元格,唯一的过滤器为:

'Product'[Category]="Clothing"
'Calendar'[Year]>2015 //from the page-level

总而言之,过滤器上下文与您可能理解的内容非常一致。我发现对于大多数人来说,单独过滤上下文是有道理的。

现在用于行上下文。每当我们迭代一个表时,就存在行上下文。您会在两个地方找到行上下文:

  1. 向表中添加计算列时
  2. 在迭代器函数中,例如:
    • -X函数(SUMXAVERAGEX等...)
    • FILTER
    • ADDCOLUMNS

每当我们谈论行上下文时,我们都在谈论迭代。您可以想到一个for循环,例如:

//pseudocode
for row in table:
    <expression>

您可能还认为行上下文类似于SQL游标,它会迭代表的行。它在很大程度上等效于快进只读游标。

我们一次考虑一行。行上下文由要迭代的表的列中的一组文字值组成。

因此,给定一个具有列(Id,金额,日期)的表“ T”,SUMX ( 'T', <expression )中的行上下文由特定值“ T” [Id],“ T” [Amount]组成和'T'[Date]。您可以通过<expression>中的列引用来引用这些值中的任何一个。您还可以将表值函数用作迭代器的第一个参数,例如SUMX ( VALUES ( 'T'[Date] ), <expression> )。在这种情况下,我们迭代的表是VALUES ( 'T'[Date] )的返回,它是'T'[Date]列中唯一值的集合。在这种情况下,行上下文仅包含来自“ T” [Date]的值-其余的“ T”不在行上下文中。

注意:当我们在行上下文中时,我们可以按名称引用一列而无需对其进行汇总-这在DAX中除行上下文外其他任何地方都无效。

注2:基本的聚合函数(例如SUMCOUNTROWS与行上下文没有交互)。因此,对于下面的表和表达式,我们将看到可能没有意义的结果:

//Table, 'T' with schema as above
{
  (1, 10, 2019-02-01),
  (2, 20, 2019-02-01),
  (3, 30, 2019-03-01),
  (4, 40, 2019-03-02)
}

//Add calculated column to 'T'
C = SUM ( 'T'[Amount] )
// Result would be 100 on every row - the total of 'T'[Amount]

//Measure on a card visual with no other filters:
M = SUMX ( 'T', SUM ( 'T'[Amount] ) )
// Result would be 400, which is the sum of 'T'[Amount] calculated once per row
// and summed together

//M2 on card with no other filters
M2 = SUMX ( VALUES ( 'T'[Date] ), SUM ( 'T'[Amount] ) )
// Result is 300, which is the sum of 'T'[Amount] calculated once per unique date
// and summed together

当我们在行上下文中并且希望该行上的值有助于过滤上下文时,可以将聚合包装在CALCULATE中,以将行上下文转换为过滤上下文。这称为上下文转换。

// Same table as above:
M3 = SUMX ( VALUES ( 'T'[Date] ), CALCULATE ( SUM ( 'T'[Amount] ) ) )
// result on card would be 100, the actual total

我们可以将计算分解为以下迭代:

// Input table would be {2019-03-02, 2019-02-01, 2019-03-01}
//Iteration1:
1. Row context: 'T'[Date]=2019-03-02
2. CALCULATE transitions 'T'[Date] value to Filter context: 'T'[Date]=2019-03-02
3. SUM is evaluated in filter context from step (2)
4. Result of iteration1 = 40

//Iteration2:
1. Row context: 'T'[Date]=2019-02-01
2. CALCULATE transitions 'T'[Date] value to Filter context: 'T'[Date]=2019-02-01
3. SUM is evaluated in filter context from step (2)
4. Result of iteration1 = 30 //note both [Amount]s for 2019-02-01 contribute to this

//Iteration3:
1. Row context: 'T'[Date]=2019-03-01
2. CALCULATE transitions 'T'[Date] value to Filter context: 'T'[Date]=2019-03-01
3. SUM is evaluated in filter context from step (2)
4. Result of iteration1 = 30

// Final result - combine iteration results with sum:
40 + 30 + 30 = 100

请注意,过滤器上下文会自动导航模型中的关系。行上下文仅由要迭代的表中的值组成。如果需要在行上下文中导航关系,则可以使用RELATEDRELATEDTABLE,也可以使用CALCULATECALCULATETABLE将行上下文转换为过滤器上下文。

因此,在您的链接示例中:

Expected Result =
SUMX (
    VALUES ( Unique_Manager[Manager] ),
    VAR SumBrand = CALCULATE ( SUM ( Budget_Brand[BudgetBrand] ) )
    VAR SumProduct = CALCULATE ( SUM ( Budget_Product[BudgetProduct] ) )
    RETURN
        IF ( ISBLANK ( SumProduct ), SumBrand, SumProduct )
)

SumBrand是当前行上下文中“ Unique_Manager” [Manager]的“ Budget_Brand” [BudgetBrand]的总和,即,管理器是迭代中当前行的值。同样,SumProduct是行上下文中管理器的'Budget_Product'[BudgetProduct]的总和。

您可以轻松定义以下内容:

Brand Budget = SUM ( 'Budget_Brand'[BudgetBrand] )

Product Budget = SUM ( 'Budget_Product'[BudgetProduct] )

Expected Result =
SUMX (
    VALUES ( 'Unique_Manager'[Manager] ),
    VAR SumBrand = [Brand Budget]
    VAR SumProduct = [Product Budget]
    RETURN
        IF ( ISBLANK ( SumProduct ), SumBrand, SumProduct )
)

我可能会进行如下重构,以便仅在需要时才计算品牌预算:

Expected Result =
SUMX (
    VALUES ( 'Unique_Manager'[Manager] ),
    VAR SumProduct = [Product Budget]
    RETURN
        IF ( ISBLANK ( SumProduct ), [Brand Budget], SumProduct )
)

无论是否进行了重构,上述引用度量的版本在语义上与内联CALCULATE ( SUM ( ... ) )的版本相同。

这是因为,如本编辑部分前面所述,以下两个是等效的:

Measure = SUM ( 'tab'[col1] )
CALCULATE ( SUM ( 'tab'[col1] ) )

我希望这有助于理解我为何如此勇敢地回答您的原始问题。作为度量,您的两个表达式在语义上是等效的。作为隔离表达式,它们不是。

答案 2 :(得分:2)

取决于您使用表达式的方式,即计算列vs测量值以及在什么情况下使用。过滤器上下文是从报表中的活动过滤器派生的,即SLICERS,CROSS FILTER通过可视化本身,甚至可以通过模型中存在的关系传播。在计算列中使用它们将产生非常不同的结果。见下图: enter image description here

如您所见,Ive提供了一个非常基本的数据集,显示职位名称及其各自的薪水。当使用不带计算饱和度的表达式时,没有提供过滤器上下文,并且不会隐式派生任何过滤器上下文,因此该列将导致整个“薪水”字段的TOTAL总和。当我们在计算语句中包装相同的表达式时,我们将提供一个ROW CONTEXT并获取表中每个ROW的总和。两种截然不同的结果。

在某种程度上使用时,它们会产生相同的结果。见下图: enter image description here 这是因为SUM()度量将隐式地用CALCULATE包裹,并继承与包含CALCULATE语句的MEASURE相同的功能。在这种情况下,树形图用作切片器,并为两个度量提供了过滤器上下文,从而产生相同的结果。

ARTICLE在解释行和过滤器上下文方面做得很好。

希望这会有所帮助!