前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从SUM让人看看PowerBI DAX的坑爹之深

从SUM让人看看PowerBI DAX的坑爹之深

作者头像
BI佐罗
发布2019-09-23 19:36:54
2.9K0
发布2019-09-23 19:36:54
举报
文章被收录于专栏:PowerBI战友联盟PowerBI战友联盟

DAX,simple but not easy.DAX,简约而不简单

如果说99%的人不真正理解PowerBI DAX的SUM,你信吗?只怕是说少了,从这个意义上讲,PowerBI就是一个坑爹的。

我们敢说自己在深入的研究PowerBI,但绝不敢说我们理解了PowerBI的一切,本文让你看看它坑爹的深度。

现给出结论:在DAX中不存在真正的SUM。很多人一定懵了,DAX 在被设计的时候被刻意地与Excel函数的用法做了贴近,而导致PowerBI的运算可以很快入门,但很快就会遇到一个普遍现象:DAX的计算结果和你期望的结果完全不同,而你无法解释为什么。

SUM的迷思

从一道经典的问题来看:

用户在订单表创建了一个计算列,并写入一个简单的公式,在按下回车的时候,每行的值应该是多少?请思考10秒继续。 有两个选择: A 每行都是一样的数字,为整个列值的和。 B 每行的数字都是当前行的销售额。

这个纠结非常符合入门伙伴会犯的错误。大家在学习了行上下文以后,了解到创建计算列的时候会创建行上下文,因此,SUM( FactOrders[销售额] ) 发生在当前的行上下文中,而由于行上下文的存在,FactOrders[销售额] 应该是当前行的值,所以,SUM( FactOrders[销售额] ) 应该和当前行的值一样。这个理解从逻辑上和已经学习的规则上是完全正确的。而你立马会被颠覆三观地看到:

你毫无办法,DAX就是这么任性,怎么办怎么办,打开《DAX圣经》来寻找答案吧:

在这一章节是这样描述的:

其含义是:这里虽然有行上下文,但 SUM 会忽略行上下文。也许,是SQLBI老师为了让大家简化理解,所以这样描述了结果。在我第一次学习到这里的时候,脑中有四个疑问: 1、刚学完行上下文规则就出现破坏规则的特例,郁闷!好吧!那么除了SUM,还有谁会忽略? 2、如果刚学完的规则马上就被破坏,那规则还有什么用,难道DAX就是这样随意的设计吗? 3、凭什么你说忽略就忽略啊,有什么证据啊? 4、这个疑问最严重,直接导致我可以把书撕了,不是刚才刚说好DAX有两个上下文,一个行上下文,一个筛选上下文,所有的运算都是发生在这两种上下文中,无一例外。(好棒,这个规则太好了),晕,这么快就来一个可以忽略行上下文的东西。那刚刚说的算什么~~~ 非常气愤!!!

如果你第一次学习DAX会有类似的疑问,那您和我是一类人,崇尚完美,不喜欢破坏规则,而也充分相信DAX的设计者不会把DAX设计成一个这么傻的东西。在很多学员中,都记住了这个特殊规则,但这里想告诉你的是:DAX有两个上下文,一个行上下文,一个筛选上下文,所有的运算都是发生在这两种上下文中,无一例外。既然无一例外,那么SUM会不会忽略行上下文呢?当然不会了,那怎么解释这个问题呢?真正的原因在于:在DAX中,是不存在SUM的,任何的SUM在计算时都会转化成SUMX。

也就是说:SUM(T[C])会被无一例外的转化为SUMX( T , T[C] ),因此在DAX实际执行的时候,是没有SUM的。

回到这个问题来看,这里确实不会忽略行上下文,而是将SUM转换成了SUMX,并进入SUMX进行计算,而SUMX将创建新的迭代过程,在表中逐行迭代,进而得到了整列的聚合结果。这个解释完美的解释了这里的问题,同时没有引入特例,没有不完美,非常好。

你是想问 SQLBI 的大师是不是说错了呢?严格意义上说,当然是了。他们引入了特例且用这种生硬的办法来定义规则,不可接受。但我也充分知道他们和我一样是知道SUM会被转换为SUMX的,他们这样描述,我猜想是要避免告诉你这么多底层的内容,就会增加学习难度。

吾爱吾师,吾更爱真理。并不是一个口号,它体现在你所有的学习和工作中。注意,我们给出这个案例绝对不是为了证明意大利人错了,而我们更牛,非也,这毫无意义。我们给出了一种理解和思考问题的方式以及真实的一个学习过程。正相反,我们无数次的研究了意大利人写书的逻辑和顺序,可以发现其编排是精心考虑的,我们能快速顺利的接受DAX,必须承认他们做了巨大的贡献,这里仅仅是一点点脚注来辅助当时和我们有一样困惑的读者。

更复杂的迷思

来看一个更复杂的迷思来挑战你对DAX的认知吧:如何计算积累销售额,其算法是这样的:

其效果如下:

从效果上看,很容易理解,这个是积累求和的典型案例。但如果你用自己学的DAX知识来解释的时候,就会发现发现问题了,我们一起来看:

第8行,在FILTER 第9行,ALL(DimDates[Date])得到了所有的日期,由于FILTER是迭代函数,进行迭代并逐一创建行上下文。 第10行,在创建的行上下文中来比较 DimDates[Date] 与 MAX ( DimDates[Date] ),问题来了,既然是在行上下文中,DimDates[Date] 只有一个值,那么 MAX ( DimDates[Date] ) 将永远等于 DimDates[Date] 本身,那么也就无法筛选到小于当前日期的所有日期。

对于这个问题,我们再看看意大利人曾经的解释是怎样的:

在读到这里的时候,我们首先想到的应该是什么,是不变的规则:DAX有两个上下文,一个行上下文,一个筛选上下文,所有的运算都是发生在这两种上下文中,无一例外。

按照意大利老师的解释,MAX直接运行在了筛选上下文中,我的天,那行上下文呢?至少在SUM的章节还提了SUM会忽略行上下文,到这里干脆连提都不提了,因此,在SUM的那个章节如果你没有真正搞懂的话,到这里说自己懂的,怎么可能呢~~~ 你此时可以有和SUM一样的理解,MAX和SUM一样忽略了行上下文,所以只有筛选上下文了。而真实的情况是:DAX中没有真正的MAX,所有的MAX在实际都会转换为MAXX,也就是MAX(T[C])的本质是MAXX(T,T[C]),因此在第10行到第11行,MAX ( DimDates[Date] ) = MAXX ( DimDates , DimDates[Date] ) ,此时可以继续解读MAXX中的DimDates,而它所处的筛选上下文,正是当月,所以MAXX ( DimDates , DimDates[Date] ) 会得到当月的最后一天。

至此,就搞清楚了这个积累求和模式。目前搞清楚这个积累求和模式的人还非常少。

如果你认为你自己是其中一个位,那么请继续看。

更更复杂的迷思

注意:在积累求和模式中,第9行,这里是 ALL ( DimDates[Date] ),而外部的筛选环境是 DimDates[Month],因此,ALL ( DimDates[Date] ) 虽然可以忽略对 [Date] 的筛选,但却不能忽略对 DimDates[Month] 的筛选,那么,在任何一个月,都只会在当月迭代,是无法出现从历史至今的效果的,一个非常巨大的矛盾出现了。

怎么解?这个问题已经超过了这里讨论的范畴。直接可以给出结论是:由于 DimDates 是日期表,系统会在 CALCULATE 的计算中自动加入 ALL( DimDates )。就不再展开了。只有极少数奇葩分子会研究下去,当然,他们也将是最终掌握DAX的人。

总结

记得在大学学习过C语言,然后有一个奇葩面试题是:在C语言中,有:a++; ++ a ; a = a + 1 ; a = a += 1,请问哪个执行效率高? C语言经过了编译器才转换为二进制代码得到执行;在DAX中,类似的是DAX表达式会被转换成对DAX引擎的查询计划才会被执行。 PowerBI的学习者有三类: 1、拖拽可视化的; 2、解决实际问题就行,搞不清楚就记住; 3、追根究底的。

我们并不是说类型3才是正确的选择,因为每个人的定位是不同的,不过,在Excel120,我们覆盖从1到3的全部内容,欢迎大家交流探讨,共同进步。 但是通过这些内容,我们可以知道的是:DAX,simple but not easy。我们在Excel120穷举式的研究PowerBI的所有细节。 学习DAX的阶段是: 1、看了能懂,写不出来 2、基础的写的出来,复杂的写不出来 3、复杂的写出来,但有的坎过不去 4、理解所有细节,性能调优

PowerBI的坑爹就是越坑越着迷越上瘾,真正学会了 DAX,PowerBI就是你的玩具,祝大家玩得开心。

为了彻底攻克Power体系,让更多的人拿来就用,我们正在考虑建立《PowerBI DAX 数据建模设计思想大全》,预览:

感兴趣的欢迎交流

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PowerBI战友联盟 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SUM的迷思
  • 更复杂的迷思
  • 更更复杂的迷思
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档