1.上下文作用的优先级别
sales_context =
CALCULATE (
CALCULATE ( SUM ( test[sales] ),
test[name] = "A", test[name] = "B" , ALL(test[name])),
FILTER ( 'test', 'test'[city] <> "XX" ))
上述公式上下文从外层到内层起作用, FILTER首先筛选出表的子集,然后ALL函数与 test[name]同时起作用
2.上下文的覆盖行为
sales_A = CALCULATE(SUM(test[sales]),'test'[name]="A")
![79435bf9ebd98e0ff064c0fd68a9cba9.png](https://img-blog.csdnimg.cn/img_convert/79435bf9ebd98e0ff064c0fd68a9cba9.png)
列表中的B、C、D筛选上下文并不生效,都是A的值,因为'test'[name]="A" 这种布尔值写法会直接覆盖该列上所有的筛选上下文。这是CALCULATE函数里上下文的第一个让人迷惑的行为,这行为的后果有两个:
a.公式外部的筛选出现问题,其计算逻辑与我们直觉不符,实际上逻辑是对的,如上图。
b.嵌套公式中,最内部的calculate公式,使用了布尔值写法,外层所有在该列的上下文都不生效,因此嵌套公式应该谨慎使用布尔值写法。
可以用filter函数替换布尔值写法:
(布尔值是filter的语法糖,但行为奇特)
sales_filter = CALCULATE(SUM(test[sales]),FILTER('test','test'[name]="A"))
![7272d56b18fc53f4e003db4ef686f782.png](https://img-blog.csdnimg.cn/img_convert/7272d56b18fc53f4e003db4ef686f782.png)
3.上下文重置
sales_all_values =
CALCULATE(SUM(test[sales]),
VALUES(test[name]),VALUES(test[team]),ALL(test[name]))
![31e0788e030a35904ff96afd66339ba3.png](https://img-blog.csdnimg.cn/img_convert/31e0788e030a35904ff96afd66339ba3.png)
使用 ALL清除某列的筛选上下文,然后在列上使用 VALUES恢复筛选上下文,是用筛选器替换同一列上筛选上下文的一种技术,与ALLEXCEPT用法相同,但是更自由。
在计算列上这一种操作是无法实现的,VALUES在计算列的计算上是无法恢复ALL函数清除的某一列上的上下文的。
注意:在复杂的报表可以更快获得类似sql语句groupby形式的数据,而不必用SUMMARIZE函数 获得临时表再计算(优化部分会着重讲values函数的作用,values函数很重要)。
人话解释:我只想要by name和by team的数据(利用ALL函数清除全部列的上下文,VALUES函数恢复特定列的上下文),而外部任何的筛选都只能影响这两列,相当于汇总到特定列的计算。
4.上下文转换
实质就是行上下文转为筛选上下文
sum函数在计算列和度量值的不同行为是DAX新手难以理解的,因为其引起行上下文转为筛选上下文的行为,并且包含DAX的一个重要特征:引用度量时,每个度量值都被一个calculate函数包裹着
sum of sales = SUM(test[sales])
[sum of sales]度量值放到列表中,实际上是CALCULATE([sum of sales]) sum在度量值
![1ef76f23e7fcc056030e428f881a5850.png](https://img-blog.csdnimg.cn/img_convert/1ef76f23e7fcc056030e428f881a5850.png)
sum在计算列
![ed21673b276812d01458852e65fe7b02.png](https://img-blog.csdnimg.cn/img_convert/ed21673b276812d01458852e65fe7b02.png)
calculate+sum的计算列
![37b44c58e27ce8e1b5feaf72129b7086.png](https://img-blog.csdnimg.cn/img_convert/37b44c58e27ce8e1b5feaf72129b7086.png)
上面三个图可以看到,sum函数在度量值的计算结果和calculate+sum的组合函数的计算结果是一致的。
SUM(test[sales])的计算逻辑就是找到这一列并求和,很简单。导致在度量值和计算列不同行为的就是DAX的特性在起作用(引用度量时,每个度量值都被一个calculate函数包裹着)。
所以calculate+sum才是我们讨论上下文转换的主角。
sales_cal_sum = CALCULATE(SUM(test[sales]))
sales_cal_sum不管作为度量还是计算列,得到的结果都是一样的(当然,作为度量需要所有列都放到列表或矩阵上)。
sum函数只能计算特定一列的和,它是如何根据不同name或city返回不同的结果呢?原因就是calculate导致的上下文转换。
上下文转换的实质是:一行有多个列组成即有多个值,然后所有值都作为筛选器,筛选表得出子集然后聚合函数进行计算(sum函数就是求和)--行上下文转为筛选上下文
calculate_sum = CALCULATE(SUM(Sheet2[sales]))
针对name=A,上面的公式转换为:
calculate_sum =
CALCULATE(SUM(Sheet2[sales]),
Sheet2[name]="A",Sheet2[city]="GZ",Sheet2[sales]=1)
![aac91cb314295fc8be2d79b395786fde.png](https://img-blog.csdnimg.cn/img_convert/aac91cb314295fc8be2d79b395786fde.png)
上图行上下文转换为筛选上下文,筛选出来所有相同的行,然后迭代计算。
某一列的值不同,即使是sum求和列的数字不同,结果也不同
sales的第二行,1改为100,上下文转换后,第一行和第二行的结果已经是不同了。
![bead92fff612b26e947eb4d65302e0d2.png](https://img-blog.csdnimg.cn/img_convert/bead92fff612b26e947eb4d65302e0d2.png)
calculate一般与迭代器函数(sumx,maxx等,sum是sumx的特殊形式)作用后发生上下文转换,如下公式:
sumx_test = CALCULATE(SUMX('test','test'[sales]*'test'[rate]))
理解了上下文转换,你就能解释如下公式:[Sum of Sales]有一个隐式的calculate函数,maxx函数迭代日期表,calculate导致上下文转换,每一行的日期都转换为筛选上下文,每天的日期通过关系传递到 salses的fact表,求和后得到最大值。
Max_Daily_Sales:=
MAXX (
'Date',
[Sum of Sales]
)
注意:上下文转换需要用到的资源非常昂贵,当一张千万级、多列,非重复行极多的表发生了上下文转换,计算力以及内存的消耗是十分大的。
总结:
calculate函数中上下文的这些行为对于我们写DAX公式有何帮助:
1.calculate函数是DAX的灵魂,每一个度量值的引用都有隐式的calculate函数包裹着,所以上下文始终存在,当计算结果不符合预期,可以考虑是否隐式的calculate函数在作用,显式地加上calculate函数并修改上下文查看结果;
2.calculate中布尔值写法的上下文的覆盖行为和上下文转换行为,是DAX运行的特性,我们需要警惕这两个行为,布尔值写法可以用filter代替,上下文转换在计算列需要时刻注意,其在度量值的转换行为则发生在包含迭代函数的公式中(calculate可能是隐式的,sumx(...)公式被引用,但是calculate隐式包含着这个公式,默默地完成了上下文转换);
3.切记所有筛选上下文作用的优先级别,有想象力地在脑海中得出所有筛选上下文作用下的一个表或模型的子集;
4.筛选上下文重置,这是最重要的,意思是我们可以按照这一特性操控筛选上下文,清除,恢复,重置,准确定位计算最终结果所需要的上下文(字段或表或模型的子集),然后行上下文迭代计算;
5.掌握了上下文的知识,试着去解释每一个函数。