DAX 计算组怎么玩?一文带你提前了解

文/Davis
BI工程师,数据分析爱好者,微软MCSE
个人博客:d-bi.gitee.io

根据微软Power BI排期,原本只能在SSAS 2019中使用的DAX特性–CalulationGroup,将在今年的Desktop中发布。本文将围绕CalulationGroup,言简意赅的讲解它的概念,以及它在PBI报表中具体是如何被应用的,能为我们带来什么。

近期国内疫情严重,宅在家里,工作要做,游戏要玩,但对BI技术的探索也不能少。
偶然想起半年前Macro Russo大师曾发表数篇关于CalulationGroup的介绍性文章,但当时本人除了Power BI以外,SSAS也只装了2012和2017两个版本,不便亲自实践,只能在理论意义上理解CalulationGroup,现在有空折腾了,就在个人电脑中又装了一个SQLServer 2019,以SSAS表格模型作为后端,PowerBI作为前端来实际体验了一下CalulationGroup。
概念

关于CalulationGroup,中文译为“计算组",也称”度量值组",在Macro的此篇文章中有很好的介绍,推荐阅读:
https://www.sqlbi.com/articles/introducing-calculation-groups/
本文在概念上仅简明介绍关于它的几个核心知识点:
  • CalulationGroup是一个表,这意味着它同样可以在DAX公式中被引用;它包含一列或两列,第一列即实际意义上的"度量值组",列中的值由多个度量值组成;第二列为可选列,用于对度量值组进行排序。

  • 度量值组中的值,即度量值本身被称为CalulationItem(计算项),它本身代表的是一个上下文环境。SELECTEDMEASURE()函数接受度量值作为参数,在特定计算项的上下文环境中执行计算。

  • 计算项默认对所有度量值有效,但你可以使用ISSELECTEDMEASURE()或SELECTEDMEASURENAME()对计算项的应用范围做出限制,使之仅对特定度量值有效。

  • 允许创建多个度量值组,但必须设定优先级,以使引擎能够理解特定度量值在不同度量值组之下的计算项上下文环境中的计算顺序(如X+1和X * 2, 是先加1还是先乘2,需要设置清楚)。在SSAS中,优先级参数为Precedence, 该值越大,优先级越高。

  • 同一个度量值组中,只能有一个计算项被激活,这是可以理解的,因为在同一个度量值组中,不同计算项不存在优先级关系,一旦多个计算项被同时应用,引擎无法理解哪个计算项被优先应用。

此外,还有一个名为SELECTEDMEASUREFORMATSTRING()的函数,它代表当前度量值的格式字符串,我们暂时无法了解未来CalulationGroup在Power BI中是如何使用的,但在SSAS中,你可以为每个计算项使用DAX表达式来设定其格式字符串。
应用

首先介绍本文使用微软官方提供的AdventureWork2017作为数据源进行建模,主要表格关系如下示:
接下来,在应用场景之下,你可能会想,CalulationGroup最主要的作用是什么?事实上它大幅减轻了DAX代码维护成本,在越复杂的报表项目中,可能涉及到要建立许多个度量值时,这种作用的优势越明显。
此外,它还增强了对数据格式控制的灵活性。在下文中,我将使用CalulationGroup为Power BI报表实现三种效果,这些效果不仅是在应用中经常需要实现的,还可以很好的表现其优势:
1. 建立多个度量值,其中包括销量,销售额,税后销售额,成本及利润,并为每个度量值计算其上月值,上月环比,去年当月值,去年同比以及度量值本身的值。
2. 实现数据可以依据不同的日期字段进行切换:根据订单日期,根据发货日期以及根据截止日期
3. 实现汇率转换,同时需要依据不同的货币切换货币符号,以人民币,美元及欧元为例
最终效果的静态展示图如下:
1. 建立多个度量值,其中包括销量,销售额,税后销售额,成本及利润,并为每个度量值计算其上月值,上月环比,去年当月值,去年同比以及度量值本身的值。
通常情况下,实现此效果需要建立(5*5)共25个度量值!或者需要利用计算表及SWITCH()编写较为冗长的代码,十分不便,利用CalulationGroup,你只需要设定销量,销售额,税后销售额,成本及利润这五个基础度量值就足够了,然后创建一个CalulationGroup,分别代表不同的计算方式,即可实现此效果。在本例中,此CalulationGroup的表达式为:
CalulationGroup1:
--共有五个计算项
ThisMonth:=
IF(ISBLANK(SELECTEDMEASURE()),0,SELECTEDMEASURE ())
---------------------------------------------------------------
LastMonth:=
VARLAST_MONTH =
CALCULATE(
SELECTEDMEASURE (),
DATEADD('DimDate'[FullDateAlternateKey],-1,MONTH)),
RETURN
IF(ISBLANK(LAST_MONTH),0,LAST_MONTH)
---------------------------------------------------------------
MOM:=
VARTHIS_MONTH = SELECTEDMEASURE(),
VARLAST_MONTH =
CALCULATE (
SELECTEDMEASURE (),
DATEADD('DimDate'[FullDateAlternateKey],-1,MONTH)
)
RETURN
IF(
ISBLANK(LAST_MONTH),
BLANK(),DIVIDE(THIS_MONTH,LAST_MONTH)-1
)
---------------------------------------------------------------
SPLY:=
VARLAST_YEAR =
CALCULATE(
SELECTEDMEASURE (),
DATEADD('DimDate'[FullDateAlternateKey],-1,YEAR))
RETURN
IF(ISBLANK(LAST_YEAR),0,LAST_YEAR)
---------------------------------------------------------------
YOY:=
VARTHIS_YEAR = SELECTEDMEASURE()
VARLAST_YEAR =
CALCULATE (
SELECTEDMEASURE (),
DATEADD('DimDate'[FullDateAlternateKey],-1,YEAR)
)
RETURN
IF(
ISBLANK(LAST_YEAR),BLANK(),
DIVIDE(THIS_YEAR,LAST_YEAR)-1
)
所有基础度量值会在以上任一被选中的计算项中执行计算,比如选择SPLY(上年同月)计算销量,则执行以下计算:
SPLY:=
VAR LAST_YEAR =
CALCULATE (
FactInternetSales[Sales Qty],
DATEADD('DimDate'[FullDateAlternateKey],-1,YEAR))
RETURN
IF(ISBLANK(LAST_YEAR),0,LAST_YEAR)
在前端选择任意月份,效果如下:
可以发现一个问题,即计算项并未按照我在度量值组中所设定的顺序显示,这时就需要设定CalulationGroup中第二列的值,即ordinal参数,该参数的设定在使用Visual Studio 2019开发环境中的SSAS中不可见,因此需要在源代码中补上这一参数(相信未来在Power BI中关于CalulationGroup的设定能够更加友好),效果如下:
2.实现数据可以依据不同的日期字段进行切换:根据订单日期,根据发货日期以及根据截止日期

实现此需求,自然需要用到USERALATIONSHIP(),同理,实现此效果需要新建一个CalulationGroup,注意,此时已经存在超过一个CalulationGroup了,因此必须指定好它的优先级。表达式如下:
CalulationGroup2:

BYOrderDate:=
SELECTEDMEASURE()
---------------------------------------------------------------
BYShipDate:=
CALCULATE(
SELECTEDMEASURE(),
USERELATIONSHIP(FactInternetSales[ShipDate],
DimDate[FullDateAlternateKey]))
---------------------------------------------------------------
BYDueDate:=
CALCULATE(
SELECTEDMEASURE(),
USERELATIONSHIP(FactInternetSales[DueDate],
DimDate[FullDateAlternateKey]))


3.实现汇率转换,同时需要依据不同的货币切换货币符号,以人民币,美元及欧元为例
此处的难点首先在于转换汇率时,度量值组如何忽略对非金额数据的影响,如本例中的Sales Qty,这一点可以使用前文提到的ISSELECTEDMEASURE()解决,其次在CalulationGroup 1中,MOM和YOY是百分比值,由于度量值组中所设定的格式字符串会覆盖度量值本身的格式,因此设定货币符号的格式字符串同时也会影响到MOM和YOY,因此必须要找到办法能够判断哪些计算项需要货币符号,哪些不需要。
事实上,如前文所述,CalulationGroup是一个表,因此结合SELETEDVALUE()函数即可解决问题,DAX表达式如下:

CalulationGroup3:

--注:数据源中的汇率表以美元作为本位币

RMB :=

IF (

NOT ISSELECTEDMEASURE ( [Sales Qty] ),

DIVIDE (

SELECTEDMEASURE () *[Avg_CurrencyRate],

CALCULATE (

MAX ( FactCurrencyRate[AverageRate]),

FILTER ( 'FactCurrencyRate','FactCurrencyRate'[CurrencyKey] = 103 )

)

),

SELECTEDMEASURE ()

)

---------------------------------------------------------------

EURO:=

IF (

NOT ISSELECTEDMEASURE ( [Sales Qty] ),

DIVIDE (

SELECTEDMEASURE () *[Avg_CurrencyRate],

CALCULATE (

MAX ( FactCurrencyRate[AverageRate]),

FILTER ( 'FactCurrencyRate','FactCurrencyRate'[CurrencyKey] = 36 )

)

),

SELECTEDMEASURE ()

)

---------------------------------------------------------------

USD:=

IF(

NOT ISSELECTEDMEASURE([Sales Qty]),

SELECTEDMEASURE()*[Avg_CurrencyRate],SELECTEDMEASURE()

)

对应的格式字符串表达式如下:
CalulationGroup 3 (FormatString表达式):
--注:报表所有金额基础度量值默认格式为货币(人民币)
RMB:=
SELECTEDMEASUREFORMATSTRING()
---------------------------------------------------------------
EURO:=
IF(ISSELECTEDMEASURE([Sales Qty]),
SELECTEDMEASUREFORMATSTRING(),
IF(
NOT OR(SELECTEDVALUE('CalculationGroup 1'[CalculationItemColumn 1]) ="MOM",
SELECTEDVALUE('CalculationGroup 1'[CalculationItemColumn 1]) ="YOY"),
"€#,0.00",
SELECTEDMEASUREFORMATSTRING()
)
)
---------------------------------------------------------------
USD:=
IF(ISSELECTEDMEASURE([Sales Qty]),
SELECTEDMEASUREFORMATSTRING(),
IF(
NOT OR(SELECTEDVALUE('CalculationGroup 1'[CalculationItemColumn 1]) ="MOM",
SELECTEDVALUE('CalculationGroup 1'[CalculationItemColumn 1]) ="YOY"),
"$#,0.00",
SELECTEDMEASUREFORMATSTRING()
)
)
至此,我们即可实现所有效果,但事情到此尚未结束,在下图中你会发现在计算销售额的MOM和YOY时出现计算错误:
之前所设定的公式都是没有问题的,但为何会出现错误呢?原因是在计算环比和同比时,所基于的数据是本币数据,而非转换为人民币,美元或者欧元后的数值,因此罪魁祸首是对CalulationGroup的优先级的错误设定。
我们需要让数据先转换为指定货币,然后再执行计算。因此这里需要将CalulationGroup 3的优先级设为最低,CalulationGroup 2其次。如下图所示,数据得到完美的纠正:
报表最终如下:
总结
利用CalulationGroup,原本需要建立上百个度量值(5 * 5 * 3 * 3)的需求,成功简化为几个基础度量值和三个度量值组,大幅节约了报表开发的时间成本以及后期代码的维护成本;
同时,利用格式字符串表达式可以实现报表更灵活更定制化的显示效果。此外,如果你有关于CalulationGroup其他的典型应用案例欢迎留言分享。

(0)

相关推荐

  • 学习DAX语言的必读书,没有之一

    我们正在进入一个低代码开发的时代,而且别无选择.因为至少有以下四大趋势: 千禧一代占劳动力比例越来越高,这是熟练使用信息技术和办公软件的一代. 未来5年,APP的开发需求超过5亿,超过过去40年的总和 ...

  • 比数据透视表强10倍!这个数据分析利器就在你的Excel菜单栏里,你却不知道……

    每天一点小技能 职场打怪不得怂 编按:一说到数据分析,很多人第一时间想到的就是数据透视表.而今天,小E给大家讲的不是数据透视表,而是一个比数据透视表强大的多的,被称为数据建模.微软20年来最伟大发明的 ...

  • Power BI Desktop 中的 DAX 基本概念

    在 Power BI Desktop 中应用 DAX 的基础知识 2019/10/21 M o 本文适用于刚开始使用 Power BI Desktop 的用户. 为你提供有关如何使用数据分析表达式 ( ...

  • 关于计算列和度量,你要知道的这些事儿!| PBI实战经验

    - 1 - 先说一下经常被问到的几个问题. 问-1:需要计算列时,是在PP里计算列好, 还是PQ里添加自定义列好? 答:这个要看实际情况,我一般建议优先考虑在PQ里添加列,因为PQ里添加的自定义列,在 ...

  • PowerBI发布重磅更新,一文带你熟悉计算组怎么用

    PowerBI Desktop,2020年7月发布了重磅更新,传闻许久的计算组终于来了,这两天不少人问,7月的版本更新以后并没有发现有什么特别的地方呀,到底怎么才能用上计算组? 这篇文章就手把手告诉你 ...

  • ETF投资修炼手册 一文带你玩转ETF投资

    中国投资成绩出色的ETF实盘指导财富公号:ETF报( etfbao ). 利用市场估值与投资者情绪变化,对ETF指数长期持有,低买高卖,做多中国,这是最适合懒人小白的财富自由之路. 第三轮:定投ETF ...

  • 梦幻西游:浩文发起的天猴组PK玩法,39级全身简易,投入超过4亿

    大家好,我是梦幻小九.梦幻西游官方组织的等级联赛是从69级开始,69级的精锐到175级的天元,低于69级的是没有联赛的.高级别的联赛对于大家来说都玩腻了,现在有玩家开始寻找更刺激的玩法,那就是组织发起 ...

  • 一文带你玩转森林图!

    提到森林图,相信大家一定不会感到陌生,在Meta分析中,森林图可以说是必不可少,它用一种非常形象的图形方式,简单直观的展示了Meta分析的统计汇总结果,受到了研究者的欢迎.那么,除了在Meta分析中, ...

  • 延时摄影怎么玩?一文带你飞起来!

    相信大家看过不少关于延时摄影的视频,无论是城市的车水马龙,还是大自然的斗转星移,都给我们带来极大的视觉震撼,下面就跟大家讲讲延时摄影这个强大技能. 1.什么是延时摄影? 延时摄影,也叫缩时摄影,是一种 ...

  • 科普|只有地球有水?一文带你了解地外水物质

    水,作为地球上最常见的物质之一,几乎是所有生命的不可或缺之物.人类在探索地外宜居地时,也一直都将液态水的存在作为重要评判标准.那么整个太阳系,甚至在更大的宇宙空间内,是不是只有地球才存在水这种物质呢? ...

  • 天青色等烟雨的天青色是什么?一文带你学会正确区别五大名窑(上)

    宋代有五大名窑之说,分别是汝窑.官窑.哥窑.钧窑.定窑. 在这五大名窑中,汝窑.官窑.哥窑这三地烧制的瓷器都是青瓷:钧窑虽然也属于青瓷,但是它并不是以青色为主的瓷器,它烧制出的瓷器颜色还有玫瑰紫.天蓝 ...

  • 天青色等烟雨的天青色是什么?一文带你学会区分五大名窑(下)

    宋代有五大名窑之说,分别是汝窑.官窑.哥窑.钧窑.定窑. 在这五大名窑中,汝窑.官窑.哥窑这三地烧制的瓷器都是青瓷:钧窑虽然也属于青瓷,但是它并不是以青色为主的瓷器,它烧制出的瓷器颜色还有玫瑰紫.天蓝 ...

  • 一文带你了解新冠疫苗的5条技术路线

    关于"一文带你了解新冠疫苗的5条技术路线",医学教育网编辑为大家搜集整理新冠疫苗种类如下,供大家参考学习. 1.灭活疫苗 灭活疫苗是最传统的经典技术路线:即在体外培养新冠病毒,然后 ...