关于 DAX 理论的资料很多,DAX 理论帮助我们理解和预测 DAX 的计算结果,但 DAX 在底层实际是怎么计算的却少有人谈及。本篇以一段在复杂上下文环境中通过 ADDCOLUMNS 进行计算的 DAX 查询代码为观察对象,使用 DAX Stuido 查看查询计划,展示其在存储引擎中执行了哪些数据查询,揭示公式引擎如何组装这些数据最终得出计算结果。
数据环境
数据文件 下载
1、DFact——数据表,由 Date 和 Amount 两列组成
2、Dates——日期表,由 DAX 代码生成
3、两张表通过 [Date] 列建立关系,Dates[Date](1) — (*) DFact[Date]
4、度量值
SumAmount = SUM('DFact'[Amount])
SumxBug = SUMX(VALUES(Dates[Year]),[SumAmount])
DAX 查询代码
EVALUATE
CALCULATETABLE (
ADDCOLUMNS ( VALUES ( Dates[Year] ), "SumxBug", [SumxBug] ),
TREATAS (
{
( 2019, 1 ), ( 2019, 2 ), ( 2020, 11 ), ( 2020, 12 ) },
'Dates'[Year],
'Dates'[Month Num]
)
)
CALCULATETABLE
以 TREATAS
作为调节器,为ADDCOLUMNS
提供筛选上下文,ADDCOLUMNS
在该上下文中扫描 Dates[Year]
并计算度量值[SumxBug]
运行结果如下:
DAX 理论
先用 DAX 理论分析一下计算结果。
下面的图中使用红色数字标记了某些列,这是故意的,后面有用处。
1、CALCULATETABLE
以 TREATAS
作为调节器,为 ADDCOLUMNS
提供外部筛选器。
2、在外部筛选上下文中计算 ADDCOLUMNS
一参 VALUES(Dates[Year])
3、ADDCOLUMNS
以 2# 数据集为迭代对象,逐行读取行上下文,在外部筛选上下文和行上下文中计算[SumxBug]
SumxBug = SUMX(VALUES(Dates[Year]),[SumAmount])
[SumxBug] 将行上下文转换为筛选上下文,并覆盖外部筛选上下文。
4、计算 SUMX(VALUES(Dates[Year]),[SumAmount])
迭代 Dates[Year]
并计算 [SumAmount]
,这里出现第二层行上下文
5、合并成最终的结果
完整的分析流程如下
注意在这个过程中存在多层行上下文的事实。
DAX 底层计算过程
以下内容将涉及 DAX 引擎和查询计划,相关资料可查看《权威指南》第二版第17、19章。
存储引擎部分
在 DAX Studio 中通过 Server Timings 窗口,了解到存储引一共执行了 3个 xmSQL 查询。
注意 xmSQL 与标准 SQL 不同,xmSQL 语句中的 SELECT 子句中出现的列会自动进行分组,举个例子,有 xmSQL 代码
// xmSQL
SELECT customer[country], customer[state], SUM ( customer[amount] )
FROM customer
与下面的标准 SQL 代码的作用是一样的
// 标准 SQL
SELECT customer.country, customer.state, SUM ( customer.amount )
FROM customer
GROUP BY customer.country, customer.state
为了方便后文指代,这里将这 Server Timings 窗口中的 3 次查询从上往下分别命名为 VQ1、VQ2、VQ3,每个查询的代码、作用和数据如下。
1、VQ1
以 ( 'Dates'[Year], 'Dates'[Month Num] ) IN { ( 2020, 12 ) , ( 2019, 2 ) , ( 2020, 11 ) , ( 2019, 1 ) }
为条件,从 Dates 表中分组获取 Year 列上的值
// xmSQL VQ1
SELECT
'Dates'[Year]
FROM 'Dates'
WHERE
( 'Dates'[Year], 'Dates'[Month Num] ) IN {
( 2020