PowerBI之DAX神功:第2卷第1回 VAR变量
DAX神功第1卷我们学到了很多知识,建议大家学习DAX神功以文字版为主,多练习,视频版为辅。我这个人很懒惰,无意义的事情我从来不做。我既然担务这么多时间写文字版,恰恰证明它重要,原理课,看视频真的不是最佳选择。2020年末已有前辈用视频讲原理,但是失败了。原因在于,人脑的速度远底于语言的速度,本身原理就需要思考和理解,原理课不同于案例的讲解。
原理课的最佳学习方法是:我写文字,你阅读,你在测试过程中去理解。
一、为什么要用变量
1.代码美观,可读性更强
这个问题因人而异,就好比你喜欢一个女演员,你认为她长得很好看,但是,总会有很多人认为你眼光有问题。所以,美观和可读性是因人而异的。
2.提高性能,这才是重点
学会一门功夫以后,可以随时且多次的使用。不需要每次使用前再重新学一次吧?
情景一:张无忌不使用变量
步骤1:五年练成九阳神功
步骤2:练乾坤大挪移时需要九阳神功做钥匙:五年练成九阳神功,1天练成乾坤大挪移
步骤3:来到光明顶,已经是明教兄弟5周年祭日了
这里只是举例,在DAX函数计算上,没有这么夸张
孙兴华个人想法:数据量较大时,建议使用Pandas或SQL等,汽车改装的确可以提高速度,但是改装成本高,也不安全;你追求速度,为何不使用速度更快的交通工具呢?
情景二:张无忌使用变量
定义以下两个变量:
变量1:九阳神功=5年
变量2:乾坤大挪移=1天 因为有九阳神功做钥匙
使用:直接调用变量即可
光明顶力战六大派=九阳神功 + 乾坤大挪移
注意1.变量名不能与表同名
我个人的建议:新建列(不同表格的列除外),度量值,表名,变量名 尽量使用唯一名称,避免混淆
你不用脑子记忆哪些可以,哪些冲突,这完全是无意义的事情
一个公司的领导没有办法在招聘时不招同姓名的员工
但是,一个学校的教务处人员,他完全有能力将同姓名的学生不分在一个班级。
你现在就有能力,不将上述内容定义为重名
注意2:变量名不能使用中文
二、变量可以赋值什么类型?
《DAX神功》第1卷第7回: 证明了变量可以是一张表
一班男生 =
var tb1 = FILTER('Sheet1','Sheet1'[性别]="男")
return
FILTER(tb1,'Sheet1'[班级]="一班")
《DAX神功》第1卷第17回: 证明了变量可以是一个值
占比1=
VAR x = sum(Sheet1[销量])
VAR y = CALCULATE(sum(Sheet1[销量]),ALL(Sheet1))
return
DIVIDE(x,y)
Ps:当然,这个时候使用度量值计算也可以,我们只是举例而已
网友提问:变量也可以是一个列呀,你使用过你怎么不承认呢?
《DAX神功》第1卷第14回
新建列 =
var x='Sheet1'[序号]
return
sumx(FILTER('Sheet1','Sheet1'[序号]=x+1),'Sheet1'[销售日期])
答:如果你问出这样的问题,那么你没认真听课,时间不够可以一周学一集,欲速则不达。变量等于某个列时,代表的是这个列的当前行,仍然是一个值。

三、当标题值做变量时
1.变量要按顺序引用
以VBA为例,基本所有语言都遵循这一原则

我们只是将变量a与变量b的顺序交换,结果就不同了,为什么?有小学数学基础便可理解。
VBA对没有定义的变量默认为0或空字符,但是DAX对没有定义的变量不能引用
DAX中无论新建列还是度量值情况都是这样的:
测试1 =
VAR a=1
VAR b=a+1
VAR c=a+b
Return
c

2.变量在其自身的VAR/Retrun代码之外不可用 【有争议】

【新建列】毛利额1=Calculate(sumx('Sheet1','Sheet1'[销量]*'Sheet1'[售价]-'Sheet1'[销量]*'Sheet1'[进价]))

【新建列】
毛利额2 =
VAR x='Sheet1'[销量]*'Sheet1'[售价]
VAR y='Sheet1'[销量]*'Sheet1'[进价]
return
CALCULATE(sumx('Sheet1',x-y),all(Sheet1[毛利额1]))
解释:《DAX神功》第1卷第14回讲到,当新建列时,变量是某列代表当前行
Ps:因为我们在一个表中新建列时,使用了多次Calculate,会出现循环依赖,所以我们要取消【毛利额1】的筛选,详见《DAX神功》第1卷第20回 循环依赖什么鬼?

接下来我们看看度量值应该怎么写?
【度量值】毛利额3 = Sumx('Sheet1','Sheet1'[销量]*'Sheet1'[售价]-'Sheet1'[销量]*'Sheet1'[进价])
解释:Sumx创建行上下文,放到度量值中,就自动转换成筛选上下文
但是变量这样写就不行了,具体原因在《DAX神功》第1卷第14回讲过了
毛利额4 =
var x='Sheet1'[销量]*'Sheet1'[售价]
var y='Sheet1'[销量]*'Sheet1'[进价]
return
sumx('Sheet1',x-y)
解释:我们讲当前行时,讨论过这样的情况
那应该怎么写呢?
毛利额4 =
var x=SELECTEDVALUE('Sheet1'[销量])*SELECTEDVALUE('Sheet1'[售价])
var y=SELECTEDVALUE('Sheet1'[销量])*SELECTEDVALUE('Sheet1'[进价])
return
sumx('Sheet1',x-y)

但是,这样写太麻烦了,没有总计是小事,假设有十多个列四则运算怎么办?
毛利额5 =
sumx('Sheet1',
var x='Sheet1'[销量]*'Sheet1'[售价]
var y='Sheet1'[销量]*'Sheet1'[进价]
return
x-y
)
解释:将变量定义在迭代函数里面,迭代函数本身就是逐行扫描,就代表着当前行

在这个知识点上,《DAX神功》与《The Definitive Guide to DAX》推导的方式不同,《The Definitive Guide to DAX》是以嵌套的概念推导,《DAX神功》是以当前行概念推导
接下来我们再看一下毛利额6:
证明:在同一个VAR/Retrun代码块之内,定义的变量可以使用
毛利额6 =
sumx('Sheet1',
var x='Sheet1'[销量]*'Sheet1'[售价]
return
x
-
var y='Sheet1'[销量]*'Sheet1'[进价]
return
y
)
解释:在迭代函数,定义变量x并输出 减去 定义变量y并输出
这符合规则:在同一个VAR/Retrun代码块之内,定义的变量可以使用

有争议的地方在于:《The Definitive Guide to DAX》指出变量在其自身的VAR/Retrun代码之外不可用
但是《The Definitive Guide to DAX》中案例举例是正确的,书中大致意思如下:
但是它的例子并不能说明,自身的VAR/Retrun代码之外不可用
因为你并没有重新定义VAR函数

书中原文总结,定义在自身VAR/Return之外不可用,VAR/Return中声明的变量在return结束后不再可见,引用它会产生错误
关于这个问题,我在PowerBI 2021.6月版本测试通过,并没有发现不可用或引用错误:
毛利额7 =
sumx('Sheet1',
var x='Sheet1'[销量]*'Sheet1'[售价]
var z=0
return
z
+
var y='Sheet1'[销量]*'Sheet1'[进价]
return
x-y
)

所以,书中说的这一点是有分歧的,拿出来与大家共同探讨,互通有无。
四、当变量做为一张表时
《DAX神功》第1卷第7回: 证明了变量可以是一张表
一班男生 =
var tb1 = FILTER('Sheet1','Sheet1'[性别]="男")
return
FILTER(tb1,'Sheet1'[班级]="一班")
Ps:但这只是新建表中对表的筛选。
接下来我们讲讲在新建列或度量值中如何定义变量为表:
【新建列】
毛利额8 = CALCULATE(sumx(FILTER('Sheet1','Sheet1'[商品] in {"A","B"}),'Sheet1'[销量]*'Sheet1'[售价]-'Sheet1'[销量]*'Sheet1'[进价]))

我们现在将【毛利额8】 改写成变量形式
毛利额8 =
VAR tb1=FILTER('Sheet1','Sheet1'[商品] in {"A","B"})
return
CALCULATE(sumx(tb1,'Sheet1'[销量]*'Sheet1'[售价]-'Sheet1'[销量]*'Sheet1'[进价]))

之前我们的变量是一个标量值时,它本身就是当前行,不需要筛选。
当你在新建列上使用变量是一张表时,要格外注意,因为新建列是行上下文,需要calculate转换后才可筛选。
名词解释:
惰性计算(Lazy Evaluation),又称懒惰计算、懒汉计算,是一个计算机编程中的一个概念,它的目的是要最小化计算机要做的工作。
VAR就是惰性计算:
<1> 没有被使用过的变量永远不会被计算
<2> 当变量完成了首次计算,则它不会在同一范围内被再次计算。
而这个案例,VAR tb1 只是起到了筛选表功能,并没有生效于calculate,所以得到了A和B商品毛利额总和21+24=45。 因为它首次使用时已经被计算(筛选表A和B商品),所以不会再被Calculate影响,失去了最终的筛选功能。
正确的写法:
[新建列]
毛利额8 =
CALCULATE(
VAR tb1=FILTER('Sheet1','Sheet1'[商品] in {"A","B"})
return
sumx(tb1,'Sheet1'[销量]*'Sheet1'[售价]-'Sheet1'[销量]*'Sheet1'[进价])
)
不过,写成度量值时,你不需要套calculate所以不牵扯这个问题
因为度量值自动将行上下文转换成筛选上下文
【度量值】
毛利额9 =
VAR tb1=FILTER('Sheet1','Sheet1'[商品] in {"A","B"})
return
sumx(tb1,'Sheet1'[销量]*'Sheet1'[售价]-'Sheet1'[销量]*'Sheet1'[进价])

五、回答网友问题:if如何用在度量值中并使用VAR变量


【1】新建列
新建列1 = if('表1'[性别]="男","先生","女士")
新建列2 =
var x = '表1'[性别]
return
IF(x="男","先生","女士")

【2】度量值
度量值1 = MINX('表1',if('表1'[性别]="男","先生","女士"))
度量值2 =
var x = SELECTEDVALUE('表1'[性别])
return
IF(x="男","先生","女士")
Ps: 关于总计不显示的问题 《DAX神功》第1卷第10回 再次讲解过

友情提示:《The Definitive Guide to DAX》中关于变量的案例是有参考价值的,可以开拓你的思路。

《孙兴华讲PowerBI火力全开》PowerBI必学课程
https://www.bilibili.com/video/BV1qa4y1H7wp
《DAX神功》文字版合集:
https://www.bilibili.com/read/readlist/rl442274
《DAX神功》视频版合集:
https://www.bilibili.com/video/BV1YE411E7p3
PowerBI(DAX函数)、PowerQuery(M函数)、Python办公自动化、Python爬虫、Python数据分析、ExcelVBA、WordVBA、AccessVBA、MySQL等等
https://www.bilibili.com/read/cv10222110