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

Prz*_*min 6 dax powerbi

nude SUM和sum wrap in有CALCULATE什么区别?

Measure1 = SUM( tab[col1]) )
Measure2 = CALCULATE ( SUM( tab[col1]) ) )
Run Code Online (Sandbox Code Playgroud)

更新。

我得到了其他SO问题的答案,该问题使用包装的计算。问题的作者对此做了解释,但我仍然不明白。这是作者建议的措施:

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 )
)
Run Code Online (Sandbox Code Playgroud)

并说明:

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

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

gre*_*gyb 2

这个答案解决了一般情况下的使用CALCULATE,涵盖了过滤器上下文、行上下文和上下文转换的主题。

正如原始问题中所定义的,根据下面的原始响应,[Measure1] 和 [Measure2] 的行为相同。请参阅对此答案的编辑,以更全面地处理CALCULATE.

一般来说,如果您正在查看行上下文中的表达式,您将使用CALCULATE单个参数来引发上下文转换(行上下文 -> 筛选器上下文)。

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

  1. 您想要使用 args 2-N 来添加、删除或更改过滤器上下文。
  2. 您想要将行上下文转换为筛选上下文。

两者可能会走到一起。

上述推论是,CALCULATE除非调用站点位于行上下文中,否则您永远不应该使用一个参数。

编辑:根据评论和更新的问题

看来这个问题有些混乱。因此,在进行上下文转换之前,我将首先澄清这一点。

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

度量不仅仅是一个命名的 DAX 表达式。度量是一个带有隐式CALCULATE包装的命名 DAX 表达式。因此,如果您想用度量的表达式替换对度量的引用,您不只是进行简单的文本替换,您可以编写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] ) )
Run Code Online (Sandbox Code Playgroud)
//Measure2 definition and expansion:
Measure2 = CALCULATE ( SUM ( 'tab'[col1] ) )

//Expansion2:
CALCULATE ( CALCULATE ( SUM ( 'tab'[col1] ) ) )
Run Code Online (Sandbox Code Playgroud)

因此,您的测量在语义上(尽管不是文本上)是相同的。两者都表现为SUM包裹在CALCULATE. [Measure2]恰好CALCULATE在扩展时多了一个。

那么它有什么CALCULATE作用呢?很多。作为参考,当我进行 DAX 培训时,CALCULATE筛选器和行上下文是一个数小时的部分。

CALCULATE执行以下操作。

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

    A。无论调用站点存在什么外部过滤器上下文CALCULATE

    b. 无论调用点存在什么行上下文CALCULATE

  2. 评估 args 2-N (称为 setfilters)以修改(添加、删除或修改现有)步骤 (1) 中的过滤器上下文,最后

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

因此,这引出了几个问题,即:

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

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

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

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

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

例如,让我们考虑一个矩阵视觉效果,其中行为“日历”[年份],列为“日历”[月份名称],切片器为“产品”[类别]=“服装”,页面级筛选器为“日历” [年份]>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"
Run Code Online (Sandbox Code Playgroud)

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

'Product'[Category]="Clothing"
'Calendar'[Year]>2015 //from the page-level
Run Code Online (Sandbox Code Playgroud)

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

现在介绍行上下文。每当我们迭代表时,行上下文就存在。您将在两个地方找到行上下文:

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

每当我们谈论行上下文时,我们都在谈论迭代。您可以将 for 循环想象为:

//pseudocode
for row in table:
    <expression>
Run Code Online (Sandbox Code Playgroud)

您还可以将行上下文视为类似于 SQL 游标,迭代表的行。它在很大程度上相当于快进、只读游标。

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

因此,给定一个包含列(Id、金额、日期)的表“T”,行上下文由SUMX ( 'T', <expression )“T”[Id]、“T”[金额] 和“T”[日期] 的特定值组成。您可以通过 中的列引用来引用这些值中的任何一个<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
Run Code Online (Sandbox Code Playgroud)

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

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

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

// 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
Run Code Online (Sandbox Code Playgroud)

请注意,过滤器上下文会自动导航模型中的关系。行上下文仅包含正在迭代的表中的值。如果需要在行上下文中导航关系,可以使用RELATED或,或者可以使用或RELATEDTABLE将行上下文转换为筛选上下文。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 )
)
Run Code Online (Sandbox Code Playgroud)

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 )
)
Run Code Online (Sandbox Code Playgroud)

我可能会重构如下,以便您只在需要时计算品牌预算:

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

不过,无论有没有重构,上面引用测量的版本在语义上都与 inlines 的版本相同CALCULATE ( SUM ( ... ) )

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

Measure = SUM ( 'tab'[col1] )
Run Code Online (Sandbox Code Playgroud)
CALCULATE ( SUM ( 'tab'[col1] ) )
Run Code Online (Sandbox Code Playgroud)

我希望这有助于理解为什么我如此勇敢地回答你原来的问题。作为度量,您的两个表达式在语义上是等效的。作为孤立的表达,它们并非如此。