DAX连接表系列(四) ⅤAR変量(3)

VAR变量(3)

经典案例

      该部分内容,其实是变量替代 EARLIER(当前行),也就是构建DAX条件筛选时的模式例子:

    (1)同比增长

      先看一个大家都非常熟悉的求同比增长的经典模式:
      还是以前面的包含三个表:产品表、销售表、时期表的数据模型举例。现在要计算每个产品的同比销售增长率。如果不使用VAR,我们的计算步骤:
      第一步:元度量([销售]);
      [销售] = SUM(Sales[销售额])
        第二步:去年销售额(在[销售]度量里添加“上一年”的筛选器);
        [上一年销售]= CALCULATE ([Sales],SAMEPERIODLASTYEAR(日期表[日期])
        第三步:同比增长率(分别将第一步、第二步作为同比的分子分母);
        [YoY%]=DIVIDE([销售]-[上一年销售] , [ 上一年销售])

      像这种公式由多个步骤构建的情况,我们可以使用VAR语法,将每个步骤事先存储起来,然后作为公式引擎的引用数据。公式如下:
      [YoY%]=
       
VAR  Sale = SUM(Sales[销售额])  -- 存储一个值列表
        VAR  Sale_year= CALCULATE(Sale, SAMEPERIODLASTYEAR(日期表[日期])-- 存储一个上一年的计算结果列表;
        RETURN -- 取出来定义的变量列表
        DIVIDE(Sale , Sale_year)  -- 将取出来的变量列表用于公式参于计算。

        注意:因为变量不能使用中文字,以及避免与其他度量重名,这里的两个变量使用了与上例不同的名称。
     
本例中,我们先定义的第一个变量--Sale,用来替代原[销售]度量(同比中的分子表达式),然后定义第二个变量(同比中的分母),同时第二个变量引用了第一个变量。
      两个变量定义完成后,相当于事先由存储引擎在内存中存储了这两个值,之后就通过RETURN后面的表达式,调用这两个值。例如,对于每个产品,都对应有两个存储数据(两个变量定义的值--这里是分子、分母计算式),分别用于由公式引擎请求执行的除法运算(行计算)。
      通过这个例子可以看出,运用VAR变量语法,可以简化度量值的书写,并便于理解公式包含的逻辑步骤。并且,由于它是在公式执行计算前,就事先被存储的存储数据,即它完成运算以后,结果就可随时被公式引擎调用,而无需重新运算,从而大大提升了DAX的性能。这些在前面已经提到。

    (2)累计类聚合计算

      这类计算离不开EARLIER函数(当前行),EARLIER函数意思可理解为:存储引擎先将某个列存储为行列表,然后依据EARLIER 定义的“行”,从数据模型里请求引用对应的该列的行值(可理解为同一个列的列值引用,即当前行。可以理解EARLIER ()将某个列虚拟了一个该列的“分身”,以用于对比筛选,由于是分身,所以其基数总是少于原列的基数)。这也符合:“变量的实际是一个存储数据(内存虚拟数据),存在于公式引擎之前,并能被公式引用的数据”的定义。

      我们已经知道,当变量被赋值后,即在被公式引用后的执行过程中,该值不会改变。基于此,先不管DAX内部是如何运行的,这时候EARLIER()更像一个常量(一个个被存储的列值),并作为DAX的行筛选条件。

        例01:一个经典的求一列中基值对应的行值出现的次数(频次计算)

      比如求两个订单时期之间的时间间隔:
      计算条件:用下一个订单的日期减去当前订单的日期。当前订单的日期自然就是原表的订单时期列,为了定义相减,需要新建一列--[下次订单日期],即先把下一行的订单日期提取过来,我们输入该计算列的DAX公式:
      第一步:求当前时期的第二次出现的时期:(需要'tb订单表里有[序号]列)
      = SUMX(FILTER('tb订单',
      ' tb订单'[序号]=  EARLIER(' tb订单'[序号])+1),
      ' tb订单'[订单日期])

      使用EARLIER(' tb订单'[序号])获取当前行的序号,然后找到当前序号+1(往后一个序号)的那一行的订单日期,即获得第二次(或下一次)订单时期;因为是直接引用' tb订单'[订单日期]的值,因此,将该列直接作为SUMX的第二参数:计算式,结果如下:

然后,新建一列,两列日期再相减,即得到两次订单时期间隔的天数:[间隔天数]列。

      使用变量替代前面的公式(这时候,只需要一个计算列,即将中间过渡的计算列定义为了变量):

      注意:既然上图公式里的Date01变量定义使用的是  ' tb订单'[订单日期] 列,那么,上图公式中加红色底线标识的部分:' tb订单'[订单日期] ,能不能也使用变量Date01替代?
      事实上,如下图,它会得出一个错误的结果:全部值为零(空值),请思考这是为什么?

        简单解释一下前面的问题:我们说过,变量被赋值之后,一般不会再变化,其实更像是“常量”,因而它可以用来定义行筛选条件。上例中的Date01变量位置,是SUMX的第二参数计算式,如果放置一个变量,其实相当于一个固定的存储数据(“常量”)被放在了计算式的位置,这是没有意义的。当然,上个公式中的Date01变量可以不用,即最后计算式改为:Date02 - ' tb订单'[订单日期] 。

      例02:求每个订单日期、客户的累计销售金额

      一个经典的计算累计销售的例子,用的是下面的DAX编写的新建列:
      SUMX (FILTER('tb订单'[序列]<=EARLIER([序列])
                          &&'tb订单'[客户ID]=EARLIER('tb订单'[客户ID])),[销售])

      使用VAR改写,返回同样的结果:

    例03:求累计求和

      将例02的多列变量模式改为针对某个计算列求累计聚合,例如帕累托累计求和的场景;

    例04:求累计值的排名

      这与例03中的累计求和的模式相同,只不过是将CALCULATE后面的计算式聚合函数改为使用COUNT(计数),而不是SUM(求和)。

      例05:求移动时期段的度量计算
    (如移动平均、前或后N天销售等)

      只需将计算改为求平均,并给出当前行筛选定义:

      有了累计值,可以求累计值占总值的百分比,再依据该累计占比值,定义ABC,这是典型的ABC之类的分组计算,这里从略。

      通过以上几个实例,你已经了解到,这些DAX都具有使用变量来替代“当前行”,并作为行筛选的特点,我们将它总结成一个模式:

        VAR V_1 = 列表、列值或函数计算式01
        VAR V_2 = 列表、列值或函数计算式02
          ……
        VAR V_N = 列表、列值或函数计算式N
      RETURN
      CALCULATE( 计算列表,
            FILTER(ALL('被筛选表'), 
              '01>V_1 && 02<=V_2))

      我们将上述公式用一个图简单说明一下该变量模式。

      前面的几个公式都是在CALCULATE里构建一个由:FILTER(ALL( ), 条件筛选)) 模式定义的筛选器,它通过ALL( )先去掉该表原有的任何关系,这时,所有针对此表的筛选(因为没有关系)将不起作用(即删除了该表上的所有筛选器),然后,再添加新的、由变量与变量所在列(如列=以该列定义的变量,这里的=号可以是其他操作符)构建的条件筛选。如果你理解不了,那就记住该模式就好。

      这与之前在说ALL()函数时,曾提到一个图一样。把条件改一下即可:

  本部分提供公式案例文件,需要的请到QQ群:490799485  Power BI 非官方群文件里下载。

  未完待续

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容