DAX高级:用作CALCULATE筛选器的日期字段

问题背景

本问题来自我的课程:《Power BI 自动化用户运营分析》 中的学生提问。

课程中给出了多种进行新用户数相关的计算方法,发现一个问题:

DAX高级:用作CALCULATE筛选器的日期字段_第1张图片

上图给出了 和日期有关的DAX使用 在不同算法(不妨依次称为1,2,3,4)中的实现。

不必深究算法,这里想强调的是,前两种算法或使用ALL(日期表),或使用FILTER(ALL(日期表))的方式忽略了日期表的影响。而在算法3,4中会发现,即使去掉ALL(日期表),计算依然是正确的,这是为什么?

等价问题

当你觉得问题复杂时,找到最小核心点,分而治之。

解决上述的问题的最佳方式不是去解决它,而是观察,测试去发现问题的范围,进而找到等价的问题,并去研究等价问题。

建立等价问题

如果觉得上述背景复杂,那我们将问题做等价简化,有A,B两个表且满足下面的关系:

DAX高级:用作CALCULATE筛选器的日期字段_第2张图片

A,B的数据以及度量值如下:

A表:

DAX高级:用作CALCULATE筛选器的日期字段_第3张图片

B表:

DAX高级:用作CALCULATE筛选器的日期字段_第4张图片

度量值:

related count in B = 
VAR current_id = MIN( A[id] )
RETURN CALCULATE( COUNTROWS( B ) , A[id]< current_id  )

诡异的结果

DAX高级:用作CALCULATE筛选器的日期字段_第5张图片

解释:由于计算上下文(本处是筛选上下文)的存在,在第2行中,CALCULATE中存在筛选器A[item]="b",此时在CALCULATE中出现的筛选A[id] < current_id,按照CALCULATE的执行原则,因为这是两个不同的字段,因此他们将共同起作用,而显而易见的是,他们共同的作用下,对表A的筛选应该是空集,而实际却并非如此。

破解诡异

将A表[id]字段的数据类型改为INTEGER,看看效果。 如下:

DAX高级:用作CALCULATE筛选器的日期字段_第6张图片

正如刚刚分析的,这才是应该呈现的效果。问题来了:为什么改成INTEGER类型就正确了,符合我们对CALCULATE运算逻辑的理解:筛选上下文是当前的A[item]而CALCULATE内部添加了筛选器A[id] < current_id,它们共同的作用的确应该为空。

探究DAX引擎的实际工作

我们最后给出的任何答案,如果想确保正确,那一定是看到了DAX引擎的实际执行,为了解决这个谜题,我们设计了4次对比。

首先,我们猜测这是与取消筛选有关的,可能会涉及到ALL,于是我们的度量值可以是两种:

第一种

related count in B = 
VAR current_id = MIN( A[id] )
RETURN CALCULATE( COUNTROWS( B ) , A[id]< current_id  )

第二种

related count in B = 
VAR current_id = MIN( A[id] )
RETURN CALCULATE( COUNTROWS( B ) , A[id]< current_id , ALL(A) )

注意在第二种度量值中使用了ALL(A)

对比试验设计

试验 内容
试验 1 数据类型:integer;度量值:不加ALL版本
试验 2 数据类型:date;度量值:不加ALL版本
试验 3 数据类型:integer;度量值:加ALL版本
试验 4 数据类型:date;度量值:加ALL版本

DAX Studio查看物理查询

如果说要理解程序运作的CPU逻辑是查看汇编或二进制代码的话,那么理解DAX执行的最底层逻辑就是查看DAX引擎执行的物理查询。(而不是逻辑查询)

按照试验的对比设计,以及常规的想法,我们应该预期试验1和试验2除了在数据类型上不同,其他应该都一致;试验2和试验4因为有ALL的加入或不加入,应该有较大差别;诸如此类。

在DAX Studio中,按照这个方式来运行:

DAX高级:用作CALCULATE筛选器的日期字段_第7张图片

限于篇幅,不再给出每个试验的物理查询文本,但将运行好的物理查询文本拷贝出来进行对比,我们对不同试验,得到以下对比结果:

试验 物理查询行数 related count in B 结果
试验 1 integer 不加ALL 23
试验 2 date 不加ALL 40 非空
试验 3 integer 加ALL 40 非空
试验 4 date 加ALL 40 非空

可以逐个将四个试验的文本依次详细对比:

试验 1 对比 试验 2

DAX高级:用作CALCULATE筛选器的日期字段_第8张图片

对比显示:

  • 不仅在数据类型处有区别
  • 试验2足足比试验1多出了更多的物理查询内容

虽然DAX表达式完全一样,仅仅是数据类型的不同,物理查询居然相差这么大。

试验 2 对比 试验 3

DAX高级:用作CALCULATE筛选器的日期字段_第9张图片

对比显示:仅在数据类型处有区别。
DAX表达式有ALL(A)的明显不同,而物理查询却仅仅是数据类型的不同。

试验 3 对比 试验 4

DAX高级:用作CALCULATE筛选器的日期字段_第10张图片

对比显示:仅在数据类型处有区别。
DAX表达式有ALL(A)的明显不同,而物理查询却仅仅是数据类型的不同。

试验 2 对比 试验 4

DAX高级:用作CALCULATE筛选器的日期字段_第11张图片

对比显示:物理查询完全一样。
DAX表达式有ALL(A)的明显不同,而物理查询却完全一致。

水落石出

通过4个试验以及4组试验对比,我们完全确认: 试验 2 date 不加ALL版本的度量值中,DAX引擎在计算时实际自动添加了ALL(A)。

总结

结合更多试验,将发现以下重要事实:若CALCULATE的筛选器中存在某表的日期字段,如:A[date],且该字段恰为主键并已与其他表建立关系,DAX引擎将在此CALCULATE的筛选器中自动加入ALL(A)。

值得特别注意的是:

  • 该表不一定是日期表,只要该字段是日期字段即可。
  • 一定是该字段已经作为主键并与其他表建立了关系。

由此,原问题得到了完整的解答。

补充

什么是主键
如果从A[C1]上连接一条线出来,连到B[C1]身上,则A[C1]叫做主键。也就是连线出发的字段。

为什么会有这么诡异的问题
这是Power BI Desktop为了实现时间智能函数而设计的内部机制。例如:
CALCULATE( [销售额] , DATEADD( 日期表[日期] , 1 , MONTH ) ) 将在该机制下,不需要考虑加入ALL(日期表)而得以正确运行。

更多参考:

  • 彻底搞懂时间智能
  • Time Intelligence in Power BI Desktop

你可能感兴趣的:(DAX高级:用作CALCULATE筛选器的日期字段)