暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

calculate函数计值顺序

BISeven 2021-11-23
963

Calculate函数是一个神奇的函数! 用的越多,对其理解也会越深,哪怕你没有完全理解,但不影响实际使用。


本文认为理解calculate函数的最佳场景是计算表或者计算列,在该场景下,其DAX不会隐式调用calcualte函数。而在使用度量值时,PowerBI总会隐式调用calculate函数,只要你想要写一个比较复杂的DAX,就会在嵌套calculate的环境中进行计算,所以理解calculate函数的最佳场景是计算表或者计算列。

 

如果你用PowerBI有一段时间了,你应该知道calculate函数的重要性,在使用calculate函数时,当筛选参数未嵌套在 KEEPFILTERS 函数中时有2个准则:

准则一:如果列(或表)不在筛选器上下文中,则将向筛选器上下文添加新的筛选器来计算表达式

准则二:如果列(或表)已在筛选器上下文中,则新筛选器将覆盖现有筛选器以计算表达式


那根据<DAX权威指南>,其Calculate函数的计算顺序如下:


 以上是calcualte函数的最重要的准则,应该牢记,除了以上两条规则外,还有一个上下文转换,即calculate函数将行上下文转换成筛选上下文,下面通过例子结合DAX权威指南中calculate函数的计值顺序。


比如下面的例子,模型很简单:


有一张日期表和销量表,销量表记录不同的水果每天的销量,两张表之间使用日期列关联


 以下的步骤,有点罗嗦,谨慎阅读,可以直接看上文的流程


一  我们使用Addcolumns函数生成一张表,记录的是每月的销量,不过添加了一个筛选条件,如下:

net table2 =
ADDCOLUMNS(values('日期表'[年月]), "销量", ----
calculate([sales], filter(values('日期表'[年月]), [年月]=202101)))

 

可以猜测一下,结果会是什么样的?

 

 

为什么会是这样的?

注意:这里没有用任何清除筛选器的函数,比如ALL

分析一下这个代码的流程:

1. addcolumns函数在日期表产生了一个迭代上下文
   因为是计算表所以对于该DAX,其初始计值上下文仅仅为行上下文) ---外部

2.Calculate函数在初始计值上下文(外部)中计算显示式筛选器参数(内部)

2.1 由1可知,初始的上下文仅仅为行上下文
  2.2 calculate函数内部的显示筛选器参数--

  这一步的结果是在日期表的年月上添加了一个筛选器: 202001

3. 执行上下文转换,使本calculate函数外部的行上下文变成等效的筛选器
 
例如对于行上下文202010,此时变成了筛选器,筛选日期表年月=202010的记录

4. 计算调节器参数(ALL *,CROSSFILTER,USERATIONSHIP),本例中无,所以跳过

5. 生成新的筛选上下文
    将步骤2中得到的筛选器覆盖步骤3-4生成的筛选器(或覆盖或添加)

在此例中步骤2中的筛选器是在日期表的年月:202001,步骤3-4生成的筛选器也是日期表的年      月:202010, 因为筛选器是同一列上,所以步骤2的会覆盖步骤3-4的筛选器

 

所以,此例的结果就是每个月的销量值都是202001的销量

 

二  我们再看一段DAX,此时我们使用嵌套Calculate函数,在上文基础的外层嵌套一个Calculate函数

net table 3 =
ADDCOLUMNS(values('日期表'[年月]), "销量",
CALCULATE(
calculate([sales], filter(values('日期表'[年月]), [年月]=202001)))
)

 

该DAX代码的结果如下,与上文结果有很大的不同,该结果仅仅只有202001月份有销量,其他月份为空值

同样的让我们一步步的来分析一下:

1. addcolumns函数在日期表中的列字段:年月生成了一个行上下文

因为是计算表,所以上下文仅仅只有一个行上下文

2. 在初始计值上下文中计算calculate函数的显式筛选器参数

2.1 根据步骤1可知初始计值上文仅仅有1个行上下文,所以计值上下文只有一个行上下文

2.2 对于嵌套Calculate函数,PowerBI先计算外层,再计算内层

2.2.1 对于外层的calculate函数,并没有显示筛选器参数,所以这里跳过
2.2.2 执行行上下文转换,使本calculate函数外部的行上下文变成等效的筛选器

例如:对于行上下文202010,此时变成了筛选器,筛选日期表年月=202010的记录

2.2.3 计算调节器参数(ALL* , CROSSFITER, USERELATIONSHIP), 本例无,跳过

2.2.4 生成新的筛选上下文,在本calculate函数中新的筛选器就只有上下文转换得到的筛选上下文
 2.2.5 在初始计值上下文计算内层calculate函数的显示筛选器
        2.2.5.1 对于内层calculate函数的初始计值上下文就变成了 2.2.4生成的筛选上下文
       2.2.5.2 显示筛选器参数为,Filter函数在日期表字段:年月生成的筛选器:202001

所以在此步骤中,生成的结果分为两类:

      对于非202001的月份,其筛选的结果为空,只有2.2.4的筛选器为            202001时,才会有结果

2.2.6 执行上下文转换,无,跳过

2.2.7 计算调节器参数(all* , crossfilter, userelationship),无,跳过

2.2.8 生成新的筛选上下文就只有202001
 所以其结果就仅仅有202001的数据,其他月份则为空

 

、如果我们将第一段代码中的calculate部分放入一个度量值,得到的结果和第二个calculate嵌套的结果一模一样,这是因为所有的度量值都会隐式的调用calculate函数,所以在度量值中理解calculate函数的行为时,一定要注意其所有的行为应都要考虑隐式调用的calculate函数

single calculate =

calculate([sales], filter(values(日期表'[年月]), '日期表'[年月] = 202001))

 

 

 

这里应该要注意的三点:
1. 显式筛选器与行上上下文转换的交互作用,由本例看,显示筛选器只要与行上下文引用的是同一列,那么显示筛选器无论是什么值都是要覆盖行上下文转换而来的筛选器,这正好印证了本文开头提到的准则二


2. 计算列或者计算表是理解calculate函数的最好场景,因为度量值总是隐式调用calculate函数,只要添加了筛选器参数,就会变成嵌套calculate,对于理解calcualte函数增加了难度。


3. 显示调用了calculate函数的度量值中,其上下文转换是由隐式调用的calculate函数执行的

 

以上是本人对于calculate函数的理解,欢迎批评指正


我是BISeven,欢迎讨论,欢迎关注

文章转载自BISeven,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论