
本文从一个零售店铺提成计算说明类似结构下多级指标计算的PowerBI建模方法。
感谢来自朋友提供的真实案例场景。
介绍
实现的效果如下:

业务管理者可以根据年份,月份来查看的某个员工所产生的实际提成费用。提成费用由以下几部分构成:
- 个人直接创造的价值按一定比例提成
- 个人完成预定的个人目标按照达到的比例阶梯提成
- 个人所在团队完成团队目标按照达到的比例阶梯为团队成员提成
- 个人所在的中心门店完成门店总目标按照达到的比例阶梯为门店成员提成
这里面的额难点在于:
- 计算个人的提成收益,要考虑比个人粒度更粗的不同级别
- 不同级别达到的阶梯不同,对于个人获得的提成比例也就不同
- 对于 汇总行 的计算,很多DAX玩家应该不难会发现在很多业务中PowerBI对汇总行的计算默认逻辑与业务逻辑是不匹配的,本案例正说明如此
- 对于目标是按月来设置的,但在体现上由1月1日表示1月,必须使用用日粒度的数据来表示月粒度的数据
业务中的人员结构
在实际业务中

中心门店与团队与个人是逐级一对多的关系。
数据结构
本案例继续沿用DAX数据建模无侵入式设计风格,建模如下:

在后续的计算中,要通过员工来考察粗粒度业务实体团队以及更粗粒度业务实体门店,并同时兼顾对完成任务与目标的计算,进而最终在提成区间表中锁定各业务人员的额提成计算逻辑进行计算。
在细粒度筛选上下文计算粗粒度对象
考虑把成员这个相对于团队和门店更细粒度对象作为筛选上下文时如何获取团队或门店的筛选上下文,并在更宽的筛选上下文中计算多个成员的聚合。如下:

在报表环境中,负责人作为最细粒度的业务对象,如何获得比它更粗粒度的业务对象组或店,并计算相应的下面的所有的人的订单销售额。

比之更粗粒度的店所对应的销售额如下:

结合上面两个公式,可以看出:
- VALUES:保持了筛选
- ALL:忽略了筛选
做到这里,可以给自己一个奖赏了,因为这里很精准地用了保持筛选和忽略筛选,巧妙跳开了当前细粒度行(筛选上下文)的影响,而同时保持了粗粒度行(筛选上下文)的影响。这种设计完全巧妙了利用了CALCULATE的特性。
群里有个伙伴问,到底CALCULATE干嘛的,都可以拖拽出来的啊,什么时候要自定义计算,那本例给出一个很好的诠释。
提成参数表
学会使用 DAX参数表 套路是一个小的里程碑,之前案例已经很多使用,本案例也会使用参数表,让模型保持动态可配置化。由于提成比例是按阶梯来的,参数表设计如下:

这里将直接提成(不分阶梯)以及个人、组、团队(分阶梯)的提成都设置了,里面用到了微弱的数字技巧,为了在实际计算时,将DAX动态筛选卡在确定的区间,并取出该区间对应的提成比例。如下:

该DAX表达式的业务逻辑很清晰,取出卡在某区间并且提成比例是个人类型的当前提成比例是多少再乘以完成额度即可。这里的巧妙在于:必须保证不管如何运算,一定有且仅有一个区间是可以能卡出来的。也就是说必须保证 存在性 和 唯一性。恰好,早就考虑到这些细节,因此处理的非常的当。然后将度量值拖拽进入,计算正确。
提成合计的计算
按照类似的思路,分别计算好直接的、个人的、团队的、店面的提成并求和即可,如下:

在模型的计算上,基本就完成了。
总计行的大陷阱你掉进来过了吗
不要高兴太早,当你开始使用时一切都是对的,直到你关注总计,似乎也是对的,直到你家财务意识到所有人的单人提成合计并不等于这里的总计值,问题出在哪里?如果不用财务提醒而能在写完DAX就自发发现这个陷阱的,那可以算是懂业务并且又可以DAX用来解决实际问题了。由于使用了自定义的CALCULATE,在行粒度的级别计算和汇总级别的计算是在完全不同的上下文进行的,结果当然不会是理所应当的正确,而是可能会出现难以察觉的问题。例如:
- 张三,提成在 10%,额度为 5000;
- 李四,提成在 20%,额度为 8000;
总计的提成比例会是多少?按照之前的度量值也会卡在一个具体的范围而得到一个明确的提成比例,而这个提成比例是显然不能用来求总计的。(自行脑补里面的逻辑)
所以,总计必须额外计算。
通常对于单行和总计额外计算,如果已经学了入门级的DAX,会想到 ISFILTER 函数来判断是否被筛选,如果被筛选,说明在细节行,如果没有被筛选,则在总计行,然后用 IF 来判断不同的情况来使用公式,如:

这时,也可以为自己能写出这样的 SUMX 来高兴一阵了。因为,至少需要理解:
- SUMX 是迭代的
- SUMX 的行上下文
- CALCULATE 的上下文转换
潜在问题被解决了。
但如果再仔细观察,会发现这个代码可以得到大幅度优化,一大堆是可以不要的,可精简为:

对的,你没有看错,这里直接用 SUMX 而不需要 IF 来判断是不是在 行 级别。这是因为,如果在行级别,也会因为筛选上下文的影响,导致 SUMX 只迭代当前行,从而实现还是在计算单行的效果。这个整个过程是需要在大脑中完成这套逻辑的。
这样,总计行的计算才变得完美。
模型扩展
在模型及指标的设计和计算中已经完成了预期的任务,但仍然有几点是可以在心里意识到的:
- 如果不选择具体的 年 或 月,计算是不能进行的,或者没有考虑这个逻辑,可能有问题,当然也可以要求用户不能有这个需求。
- 如果在年度的级别来计算汇总,也会遇到总计行失效的问题,那是否要做什么调整呢,当然设计时没考虑过了一年的事所以可能忽略了。
当然,整个模型的设计思路是OK的,在提成阶梯方面,给出了最大的可配置和扩展空间。
总结
通过本案例的展示,可以看清在PowerBI助理数据建模时候:
- 可以给出很灵活的设计
- CALCULATE 中 VALUES 和 ALL 的灵活配合来保持筛选或忽略筛选
- 见山是山:只算细粒度行,不考虑总计行
- 见山不是山:意识到算细粒度时候,也时刻警觉总计行的陷阱
- 见山还是山:原来总计行有陷阱但算单行和算总计可以用一个公式
- ...
不难举一反三:
- 如果是财务,正在做账,那么 PowerBI 必然可以助力
- 如果是HR,正在算复杂的绩效或工资按阶梯扣税,那么 PowerBI 参数表法也可以胜任
- 如果是项目管理,在不同数据粒度级别计算项目成本,周期,费用
可以在实践中慢慢体会了。
如需获取源文件可关注微信公众号查看同一文章。

网友评论