169_技巧_Power BI 依据促销日历计算销售金额
一、背景
在各位表格表姐们计算销售金额的时候,有一个绕不开的问题:商品有促销的问题,需要使用对应的促销价来计算销售额。
按照惯例还是先来看看结果
Power BI 公共 web 效果:https://demo.jiaopengzi.com/pbi/169-full.html
二、场景解析
上图中结果其实非常简单,用一个矩阵就表示了结果。从DAX 建模和数据清洗还是有一定的要求。
数据采集需要符合业务需求和采集便利性。业务人员或者说我们数据的管理人员,更希望数据是在一行里面尽可能简短的表达完整我们的信息(拉链表)。但是在我们使用 DAX 做模型的时候,更希望看到的是流水表,这两点就是矛盾的。
接下来我们就使用 Power Query 来解决这个问题。
1、促销日历 & 价格表
案例中,价格表、促销日历都是使用拉链表的形式来记录信息。这不利于我们建模分析,于是需要使用 Power Query 来处理一下数据。
注意:促销日期
和 展开拉链
,这两个步骤,从拉链表展开到流水表。
let
源 = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjIwMtY31jdU0oExLYDMp73Tn/bsAjKezdj3fMmupzNXANkGepamSrE6UD1GCD1ApgmQ/WTHHIimFw2bn83ZBdZhYaEUGwsA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [促销开始日期 = _t, 促销结束日期 = _t, 区域 = _t, 产品 = _t, 折扣 = _t]),
更改的类型 = Table.TransformColumnTypes(源,{{"促销开始日期", type date}, {"促销结束日期", type date}, {"折扣", type number}}),
促销日期 = Table.AddColumn(更改的类型, "促销日期", each List.Dates([促销开始日期], Number.From([促销结束日期]-[促销开始日期])+1, #duration(1,0,0,0))),
展开拉链 = Table.ExpandListColumn(促销日期, "促销日期"),
类型 = Table.TransformColumnTypes(展开拉链,{{"促销日期", type date}}),
删除的列 = Table.RemoveColumns(类型,{"促销开始日期", "促销结束日期"})
in
删除的列
注意:日期
和 展开
这两个步骤,从拉链表展开到流水表。
let
源 = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjIwMtY31DdU0oEyjfSNQZyXexY8W9AOZBgaGCjF6uBROGXd89kzwKL4Fb5o2Pxszi4gw4yAwmcz9j1fsuvpzBVAtiVCrRFCrRGmM40IKYQ505iAQrgzTQkoRHYmMJCAimMB", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [开始执行日期 = _t, 结束执行日期 = _t, 产品 = _t, 价格 = _t]),
更改的类型 = Table.TransformColumnTypes(源,{{"开始执行日期", type date}, {"结束执行日期", type date}, {"产品", type text}, {"价格", Int64.Type}}),
日期 = Table.AddColumn(更改的类型, "执行日期", each List.Dates([开始执行日期], Number.From([结束执行日期]-[开始执行日期])+1, #duration(1,0,0,0))),
展开 = Table.ExpandListColumn(日期, "执行日期"),
类型 = Table.TransformColumnTypes(展开,{{"执行日期", type date}}),
删除的列 = Table.RemoveColumns(类型,{"开始执行日期", "结束执行日期"})
in
删除的列
2、常规表格
销售明细按照常规的方式处理字段类型即可,同时准备好我们的日期表,维度表:区域表、产品表等。
3、表间关系
如下关系为: 多对一
- ‘销售明细’[产品] --> ‘产品表’[产品]
- ‘销售明细’[日期] --> ‘日期表’[Dates]
- ‘销售明细’[区域] --> ‘区域表’[区域]
- ‘促销日历’[促销日期] --> ‘日期表’[Dates]
- ‘促销日历’[区域] --> ‘区域表’[区域]
- ‘产品价格’[执行日期] --> ‘日期表’[Dates]
- ‘产品价格’[产品] --> ‘产品表’[产品]
- ‘促销日历’[产品] --> ‘产品表’[产品]
三、DAX
这里我们使用了两种方式来写度量值。一种是使用 LOOKUPVALUE
,这种写法可以如下关系去掉。
- ‘促销日历’[促销日期] --> ‘日期表’[Dates]
- ‘促销日历’[区域] --> ‘区域表’[区域]
- ‘产品价格’[执行日期] --> ‘日期表’[Dates]
- ‘产品价格’[产品] --> ‘产品表’[产品]
- ‘促销日历’[产品] --> ‘产品表’[产品]
销售金额-LOOKUPVALUE =
VAR saleData =
CALCULATETABLE ( '销售明细' )
VAR saleDataAdd =
ADDCOLUMNS (
saleData,
"@price", LOOKUPVALUE ( '产品价格'[价格], '产品价格'[产品], '销售明细'[产品], '产品价格'[执行日期], '销售明细'[日期] ),
"@promotion",
VAR discount =
LOOKUPVALUE (
'促销日历'[折扣],
'促销日历'[产品], '销售明细'[产品],
'促销日历'[促销日期], '销售明细'[日期],
'促销日历'[区域], '销售明细'[区域]
)
RETURN
IF ( ISBLANK ( discount ), 1, discount )
)
VAR result =
SUMX ( saleDataAdd, [@price] * [@promotion] * [销售量] )
RETURN
result
另外一种使用 RELATEDTABLE
,这种方式充分的利用到了维度表和事实表之间的关系。
销售金额-RELATEDTABLE =
VAR saleData =
CALCULATETABLE ( '销售明细' )
VAR saleDataAdd =
ADDCOLUMNS (
saleData,
"@price", MAXX ( RELATEDTABLE ( '产品价格' ), '产品价格'[价格] ),
"@promotion",
VAR discount =
MAXX ( RELATEDTABLE ( '促销日历' ), '促销日历'[折扣] )
RETURN
IF ( ISBLANK ( discount ), 1, discount )
)
VAR result =
SUMX ( saleDataAdd, [@price] * [@promotion] * [销售量] )
RETURN
result
对比两种写法的效率,明显能看到 销售金额-RELATEDTABLE
比 销售金额-LOOKUPVALUE
更快。同时 DAX 代码量也相对更少。
四、总结
-
在数据收集和规范阶段,可以把数据要求为拉链表的形式,更便于对业务人员对数据的维护,同时也能很好的使用 Power Query 来做数据清洗便于建立分析模型。
-
类似促销日历的这种形式,在 dax 建模的时候,我们使用一层一层的迭代的形式往数据上去叠加,同时需要考虑数据最小粒度,这里的价格表和促销日历,我们是使用日期来进行约束描述的。这样能应对不同形式的价格调整,不会对模型造成的影响。
-
当前案例对表间关系的理解需要到位,不然容易使用
LOOKUPVALUE
,当然无论什么方式,只要能解决业务问题都是好的方式,只是效率相对低一些。
请关注
全网同名搜索 焦棚子
如果对你有帮助,请 点赞、关注、三连 支持一下,这是我们更新的动力。
by 焦棚子