东哥陪你学DAX——上下文系列小讲堂(二)
初识行上下文
在介绍两种上下文过程中,我会尽量列举它们在计算列和度量值中的不同表现,来增强大家的感性认识。(本系列所用示例,来自微软官方样本数据库AdventureWorks)
“行上下文”是怎么来的?
1. 创建计算列
2. 使用迭代器(以X结尾的函数)
先来看计算列
010.png上表中,“利润”和“销售额”均为计算列
销售额 = [销量] * [单价]
利润 = [销量] * ([单价] – [成本])
对初学者来说,上述公式能得到正确结果,是天经地义,再正常不过了。因为EXCEL里的列计算就是这样,加减乘除结果怎么可能会错
上面这句话,只说对了一半
错在哪里?错就错在,我们把EXCEL的思维方式套在了DAX身上
在EXCEL里,对于列的计算,我们都是在首行单元格输入公式,通过下拉填充,得出全列结果。每个单元格的公式,除了运算符号不变,单元格引用都不同。该列的每一行,只使用当前行单元格来参与运算
011.png 012.png而DAX是以列运算为主,整列就一个公式,怎样确保参与运算的都是当前行,而不会出现第三行销量第五行单价第八行成本算到一块儿去?
之所以整列计算结果都正确,恰恰因为“行上下文”的存在并起了关键作用
只要创建计算列,就会自动创建行上下文,无需手工干预。行上下文就是如来手掌,计算列的运算无论如何也跳不出去
就拿上面两个公式来说,计算列一旦创建,DAX立刻就从第一行开始迭代,同时也创建了一个包含当前行的行上下文并开始计算。计算动作发生之前,行上下文已锁定当前行,因此整列计算才不会出错
那要如何通过度量值来建立“行上下文”?
用迭代器
迭代器其实就是迭代函数,末尾带字母X的函数都自备迭代功能,诸如SUMX,AVERAGEX,COUNTX,MAXX,MINX等
017.png上图中,SUMX函数作为迭代器,将创建了一个“行上下文”,并对SUMX第一个参数“销售表”进行迭代,在行上下文中,执行SUMX的第二个参数(单价乘销量的表达式)。因此,在迭代过程中,每行表达式都明确知道自己应该用哪一行的销量和单价来运算
033.png再重复一次:
为什么同样的表达式,在计算列里就能正确执行,而到了度量值里却不行?
因为计算列会自动创建行上下文,而度量值却不会
如果想在度量值内逐行计算该表达式,必须通过迭代函数来人为创建行上下文
还有一点需要注意,“行上下文”里的“行”,是针对数据模型里的表而言(后面会提到虚拟表的概念,虚拟表中也可以使用行上下文),而不是可视化元素下的表格和矩阵。前者是模型原表,后者是聚合后再被筛选过的数据容器。DAX无法直接引用后者的行或列,避免矫枉过正
044.png行上下文入门小结:
- 计算列和迭代函数都可以创建行上下文
- 执行计算的行没有储存在公式内部,而是由行上下文定义
- 行上下文只包含一行(基于行号,永远不会重复),并且在被创建时自动定义
- 行上下文只用来决定引用目标列的哪个值(即确定计算发生在哪一行)
- 表格的列并没有某个确定的值,列在表格每一行都有一个值,如果你想让列取得某个值,需要确定这个值所在的行,而确定行的唯一方式是使用行上下文
- 要使用“行上下文”,要么通过计算列、要么通过迭代函数
(未完待续)