Power BI之DAX神功:第3卷第17回 扩展表中的上下文转换
官方解释:扩展表所有列的行上下文都会被转换成等效的筛选上下文。
不要被文章的标题和官方解释吓到,扩展表什么样,你也改变不了!比如说:孙兴华今年21岁,你生气,你说不行,孙兴华不能是21岁,那你也让我变不成18岁呀!
这一课的重点是告诉我们:上下文转换也许比行上下文更消耗性能!
提到上下文转换,我们首先想到Calculate和CalculateTable,但是我们之前讲过的一些时间智能日期函数FIRSTDATE、LASTDATE、FIRSTNONBLANK、LASTNONBLANK 等等,它们内部都有隐藏的Calculate实现上下文转换。
《DAX神功》答网友问12.为什么LastDate与Max结果不同 中我们就做了详细证明
不要因为一些讲师告诉你们尽量不要新建列,要用度量值。你就认为行上下文速度慢,转换成筛选上下文速度快!不一定!
要我说,你随心所欲就好,就像我用VBA一样,20多年前老师在学校让我定义数据类型,学校电脑奔腾133MHz+16M SDRAM定义什么都慢,机器就不行!现在我的电脑跟你们比还是很差AMD2200G+16G DDR4 但是在VBA中是否定义数据类型我还是没感觉出区别来。
今年刚上市的3A大作也许最低显卡要求1650,推荐3080。但是我告诉你10年之后,主流集成显卡都能玩这款3A(别比帧数,我指的是1080p下能流畅的玩)。我现在用的AMD2200G集成Vage8配上双通道DDR4内存可以达到80% 1030显卡的水平,10年前的游戏基本都能玩。
游戏可能10年后你因为画质问题或者有后续新作就不喜欢前作了,Excel、DAX和游戏还不一样!它们的生命周期也许是30年至50年!甚至更久!
DAX不是SQL,数据峰值也就百万行数据。你现在绞尽脑汁研究的优化,会被时间和硬件埋没!这话今天你可能不信,Excel不淘汰,DAX就不会淘汰,10年之后如果有人翻出这篇文章,秒懂!
Ps:并不代表DAX优化没用,只是对于你来说,付出和回报是否成比例,由你个人决定。
一、上下文转换


《DAX神功》答网友问05 返回表的迭代函数你应该注意什么?我们证明了:
ADDCOLUMNS:相当于整容,例如你长了两只眼睛,整容可以让你变成3只,但你还是你。
SELECTCOLUMNS:相当于克隆再整容,克隆你再整容成3只眼,长的一样,但是那不是你。
我们今天的测试用哪个都一样,我们就选择ADDCOLUMNS吧!

上图中学生表这样的情况很普遍吧,同名张三,但是学号不同,就是两个人。
学生表是1端表,他的学号是主键,主键不为空不重复!学PowerBI之前我让大家去学Access基础篇s01开头的集数,没坑你们。我们使用学生表的学号(主键)连接成绩表的学号(外键)构成一对多关系。
如果说学号是唯一值(废话!主键不为空不重复它肯定是唯一值),我就返回学号对应的姓名。同理,学号是唯一值返回学号!度量值如下:
关于IF + HASONEVALUE 原理详见《DAX神功》第2卷第19回
这样的写法:PowerBI 2021.10月版测试通过,如果版本太旧,可能不支持此语法

因为度量值本身是筛选上下文,if语句生成行上下文,我们需要套上Calculate做转换
返回结果:

上面的方法才是正解!《The Definitive Guide to DAX》中将IF(HASONEVALUE(字段名), VALUES(字段名))的方法替换成了SELECTEDVALUE(字段名)。在《DAX神功》第2卷第19回我们的确讲过这样的替换,但是在这里,不行!你只能确定主键不为空不重复,其它列你确定不了!
如果我们这样使用代码:
返回结果:直接报错!因为姓名有重复!

当初我讲完《火力全开》后,很多讲师来抬扛,理由是权威指南说什么,国外文献说什么!
别管别人说什么,在不同的环境下有不同的方案,任何人写出的代码也不可能适配所有的环境。既然这样,只要对方能解决自己的案例,就没有问题,谈不上对错。
最无知的一句话:你这个代码我换一个表就不行了!
上面讲的这些,就是上下文转换!将行上下文转换成了筛选上下文,但是转换过程中,你有多少个列,就相当于有多少个筛选器,让这些列可以筛选。你的列越多,硬件开销就越大,时间就越久。所以说,使用行上下文和转换成筛选上下文谁的开销大,是依据情况而定的。
二、理解行上下文,一定要知道Excel工作表函数Vlookup原理
返回结果:

我摊牌了,这就是行上下文!ADDCOLUMNS给成绩表添加列,通过RELATED函数获取这一行对应的姓名和学号。那你知道RELATED函数是根据谁把姓名和学号1这两列生成的吗?答案是:成绩表中的外键(学号),对!行上下文只考虑这一列!
说清楚行上下文,我们还得用VBA的方式来说:
就好比咱们在VBA里面的For循环、Do While循环、数组、字典....
例如有如下两张表,我现在说的是VBA,这两张表可不需要连线!大家脑子要转过来!

现在想将表1中的电话V到表2中,Vlookup函数:找什么,在哪列里找,返回找到的第几列。Vlookup是人家写好的函数,你拿来用,没见过哪位讲师将底层函数拿出来讲代码的吧?那为什么DAX函数要讲原理?答案:商业化!
为了更深入的理解行上下文,我们使用VBA举例:
友情提示:我会用find函数,我需要先举一个极端例子,显示出速度慢!
返回结果:

我们从表2的B列第2行开始,姓名是张三,我们将张三在表1中逐行去找,找到了就返回张三对应的电话,没找到就返回空。接下来再找表2的B列第3行,还是这么找。
你试想一下,以王五为例,王五在表2的第1048576行,得到王五的姓名后,我要将王五在表1中每一行进行查找,又在表1的1048576行找到了它。我一共循环了1048576*2次。逐行扫描就是行上下文。
再看看字典:
当我们使用字典时,姓名是Key,电话是Value。(因为是举例,我们假设没有重复姓名),我们将表1中的姓名和电话存入字典。只需要找表2中的姓名在不在字典中,在就返回对应的电话,不在就返回空。其实字典更像Vlookup。(当然Vlookup函数底层代码会比我这个写的要复杂的多)
很久很久以前,手机没有普及时,我们将电话号码记在本子上,打电话用座机,在本子上找号码。如果本子上只有你爸妈,那没问题!如果你的本子上有1万人,你很难快速找到你要找的人。即便你的本子上使用了汉语拼音姓氏排序也需要时间。我记得2000年以前通信公司出《大黄页》这种书,就是按拼音排序企业名称,对应有企业电话。说白了就是文字版的114查号台。
80、90年代我就是在本子上找到电话后,去摇对方电话号码!

错了。。。。上面这是我前世用过的电话。。。我今生第一次用的电话是转盘的

当初我第一次见到真电话时,不知道打给谁,因为认识的人都没有电话。现在除了工作电话之外,我也不知道打给谁,因为认识的人都挺忙。
现在我们不用电话本了,我们手机中存着电话,而且基本都有免费云端做备份,手机丢了也能找回电话备份。手机上就算有10万人,你输入姓名就可以立即查找。
这就是行上下文!比如:你问我张三、李四、王五的手机号是多少,我在手机上输入张三,把手机号抄下来,然后再查李四.....
总结:到底是行上下文快还是转换上下文快?具体问题具体分析!没有固定答案!104万行只有一列的表,转换上下文有可能快!104万行10列的表,一定行上下文快!因为转换上下文筛选每一个列,而行上下文只看主键或外键那个列。
主键和外键是数据库知识!我在《火力全开》首集就告诉大家,一定要去看Access基础篇S01开头的内容。

《孙兴华讲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等等