Power BI 依据促销日历计算销售金额

169_技巧_Power BI 依据促销日历计算销售金额

一、背景

在各位表格表姐们计算销售金额的时候,有一个绕不开的问题:商品有促销的问题,需要使用对应的促销价来计算销售额。

按照惯例还是先来看看结果

169-1

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
    删除的列

169-2

注意:日期展开 这两个步骤,从拉链表展开到流水表。

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
    删除的列

169-3

2、常规表格

销售明细按照常规的方式处理字段类型即可,同时准备好我们的日期表,维度表:区域表、产品表等。

169-4

3、表间关系

如下关系为: 多对一

  • '销售明细'[产品] --> '产品表'[产品]
  • '销售明细'[日期] --> '日期表'[Dates]
  • '销售明细'[区域] --> '区域表'[区域]
  • '促销日历'[促销日期] --> '日期表'[Dates]
  • '促销日历'[区域] --> '区域表'[区域]
  • '产品价格'[执行日期] --> '日期表'[Dates]
  • '产品价格'[产品] --> '产品表'[产品]
  • '促销日历'[产品] --> '产品表'[产品]

169-5

三、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 代码量也相对更少。

169-6

四、总结

  1. 在数据收集和规范阶段,可以把数据要求为拉链表的形式,更便于对业务人员对数据的维护,同时也能很好的使用 Power Query 来做数据清洗便于建立分析模型。

  2. 类似促销日历的这种形式,在 dax 建模的时候,我们使用一层一层的迭代的形式往数据上去叠加,同时需要考虑数据最小粒度,这里的价格表和促销日历,我们是使用日期来进行约束描述的。这样能应对不同形式的价格调整,不会对模型造成的影响。

  3. 当前案例对表间关系的理解需要到位,不然容易使用 LOOKUPVALUE ,当然无论什么方式,只要能解决业务问题都是好的方式,只是效率相对低一些。

请关注


全网同名搜索 焦棚子

如果对你有帮助,请 点赞关注三连 支持一下,这是我们更新的动力。

by 焦棚子