计算客户销售平均值时SUM与SUMX的差异及CALCULATE的作用
一、SUM与SUMX在AVERAGEX中的差异(示例1、示例2)
示例1
AverageSalesPerCustomer := AVERAGEX ( Customer, SUM (Sales[SalesAmount]) )
这段代码的逻辑是:用AVERAGEX迭代Customer表的每一行,在当前客户的行上下文中,SUM(Sales[SalesAmount])会自动继承该客户的筛选范围,直接聚合该客户所有销售记录的SalesAmount总和,最后对所有客户的销售额求平均。SUM是直接针对筛选后的列做聚合运算,属于聚合函数。
示例2
AverageSalesPerCustomer := AVERAGEX ( Customer, SUMX ( Sales, Sales[SalesAmount] ) )
这段代码的逻辑是:同样用AVERAGEX迭代Customer表,在当前客户的行上下文里,SUMX会逐行遍历该客户对应的Sales表记录,对每一行的SalesAmount做累加求和,最后再对所有客户的销售额求平均。SUMX是迭代函数,会逐行处理数据后再聚合。
在这个基础场景下,示例1和示例2的计算结果是一致的,但两者的执行逻辑有本质区别:
- 如果
SalesAmount是简单的基础列,两者效果相同; - 如果
SalesAmount是需要逐行计算的表达式(比如Sales[Quantity] * Sales[UnitPrice]),SUMX可以支持逐行计算后再求和,而SUM只能直接聚合已存在的列值,无法处理动态的逐行计算。
二、用CALCULATE包装后的效果(示例3、示例4)
示例3
AverageSalesPerCustomer := AVERAGEX ( Customer, CALCULATE(SUM (Sales[SalesAmount])) )
CALCULATE的核心作用是上下文转换——它会把AVERAGEX迭代产生的Customer行上下文,转换为筛选上下文。
在这个基础场景下,计算结果和示例1一致,但CALCULATE赋予了我们修改计算上下文的能力:比如可以在CALCULATE中添加额外筛选条件(如CALCULATE(SUM(Sales[SalesAmount]), Sales[OrderDate] >= DATE(2023,1,1))),或者清除部分筛选器(如CALCULATE(SUM(Sales[SalesAmount]), ALL(Sales[Region]))),从而灵活调整求和的范围。
示例4
AverageSalesPerCustomer := AVERAGEX ( Customer, CALCULATE( SUMX ( Sales, Sales[SalesAmount] ) ) )
这里CALCULATE同样会将Customer行上下文转换为筛选上下文,SUMX则在这个筛选后的Sales表范围内逐行累加SalesAmount。
基础场景下结果和前三个示例一致,但结合CALCULATE的上下文控制能力,我们可以对SUMX的迭代范围做更灵活的调整——比如限定只迭代某类产品的销售记录,或者排除退货订单等。
内容的提问来源于stack exchange,提问作者variable

