SUMMARIZE执行两个操作:按列分组和添加值列。使用SUMMARIZE对表进行分组是一个安全的操作,而使用SUMMARIZE添加新的列可能会导致难以调试的意外结果。
为了正确理解 SUMMARIZE,您必须了解集群的工作原理,行上下文和筛选上下文的存在有什么影响,以及扩展表在集群中的作用。
1 聚类(Clustering)2 行上下文和筛选上下文3 ADDCOLUMNS 和 SUMMARIZE的组合
SUMMARIZE的常用语法形式:
SUMMARIZE ( <Table>, <GroupBy_ColumnName1>, [<GroupBy_ColumnName2>...], [<Name1>, <Expression1>], [...])
聚类是 SUMMARIZE 用来计算其结果的一种技术,我们使用只有七行的表来引入聚类。
SalesbyColor =
SUMMARIZE (
Sales,
Sales[Color],
"Sales", SUM ( Sales[Amount] )
)
直觉上,我们可能会认为 SUMMARIZE 是从按 Sales[Color] 对 Sales 进行分组开始的;然后它通过在颜色上创建筛选上下文来计算具有相同颜色的所有行的 Amount 总和。不幸的是,这只是 SUMMARIZE 执行的步骤的近似值。它的实际情况更复杂。
因为查询需要按颜色分组,所以 SUMMARIZE 将表拆分为分区——每种颜色一个。此操作称为聚类。聚类是基于用于分组的列创建分区。SUMMARIZE 首先根据颜色对表进行聚类,然后通过创建筛选上下文来计算每个聚类的表达式。因为我们按 Sales[Color] 分组,SUMMARIZE 根据颜色将 Sales 表拆分为三个集群。
因为我们按颜色分组,所以每个集群都由一种颜色标识。在我们的场景中,Sales[Color] 是集群标头。簇头是 SUMMARIZE 的 groupby 部分中使用的一组列。簇头可以包含多列,当前场景中我们只有一列。
SUMMARIZE ( <Table>, <GroupBy_ColumnName1>, [<GroupBy_ColumnName2>...], [<Name1>, <Expression1>], [...])
集群准备就绪后,SUMMARIZE 计算三个集群的 SUM (Sales[Amount]) 值。为了将计算限制在单个集群中,SUMMARIZE 不会创建仅包含集群标头的筛选上下文。相反,它使用集群中的所有列创建筛选上下文,筛选集群中存在的值。
为 Blue 集群计算的表达式如下所示:
CALCULATE (
SUM ( Sales[Amount] ),
FILTER (
ALL ( Sales ),
( Sales[Color], Sales[Product], Sales[Quantity], Sales[Amount] )
IN {
( "Blue", "Bike", 3, 300 ),
( "Blue", "Shirt", 4, 400 )
}
)
)
表达式筛选所有列,要求所有列的值都属于簇中的一行。您可以通过运行以下查询来仔细检查此行为,作为测试:
Test_Quality =
SUMMARIZE (
Sales,
Sales[Color],
"Test", ISFILTERED ( Sales[Quantity] )
)
查询在 Test 中返回 True。这表明 Sales[Quantity] 正在被主动筛选,即使它没有出现在 groupby 列中的任何地方。事实上,Sales[Quantity] 在由 SUMMARIZE 计算的表达式中被筛选,因为 Sales[Quantity] 是为按颜色切片而创建的集群的列之一。虽然每个簇确实只筛选一种颜色,但实际上簇筛选的不仅仅是颜色。
在继续之前,尝试猜测以下查询的结果:
Sales_All =
SUMMARIZE (
Sales,
Sales[Color],
"Sales", SUM ( Sales[Amount] ),
"All Sales",
CALCULATE (
SUM ( Sales[Amount] ),
REMOVEFILTERS ( Sales[Color] )
)
)
如果根据常识猜测,我会说 All Sales 返回所有产品的销售额。相反,All Sales 的结果不是销售额的总计;这是一个非常奇怪的数字,只有了解聚类才能理解。
数据源:
如果您依赖直观的行为,您会假设 REMOVEFILTERS (Sales[Color]) 从 Sales[Color] 列中删除了筛选器,从而使 Sales 的所有行都可见。实际上,REMOVEFILTERS 会从 Sales[Color] 中删除筛选器,但不会从集群中的所有其他列中删除筛选器。因此,对 Blue 集群的 All Sales 的评估如下:
CALCULATE (
CALCULATE (
SUM ( Sales[Amount] ),
REMOVEFILTERS ( Sales[Color] )
),
FILTER (
ALL ( Sales ),
( Sales[Color], Sales[Product], Sales[Quantity], Sales[Amount] )
IN {
( "Blue", "Bike", 3, 300 ),
( "Blue", "Shirt", 4, 400 )
}
)
内部 CALCULATE 删除 Sales[Color] 上的筛选器,但它不会修改 Sales[Product]、Sales[Quantity] 和 Sales[Amount] 上的所有剩余筛选器。如果删除 Sales[Color] 上的筛选器并保留其他列上的筛选器,则组合 (Green, Bike, 3, 300) 是在筛选上下文中变得可见的唯一附加行。
每个集群中存在的列取决于您用作 SUMMARIZE 起点的表。事实上,尽管我们通常在模型中对表进行 SUMMARIZE,但 SUMMARIZE 实际上可以对任何表表达式进行分组。您对此有完全的控制权。例如,我们可以稍微改变查询以获得变体。我们不汇总Sales,而是汇总所有(销售额[颜色]、销售额[数量]):
Sales_colorQuantity =
SUMMARIZE (
ALL ( Sales[Color], Sales[Quantity] ),
Sales[Color],
"Sales", SUM ( Sales[Amount] ),
"All Sales",
CALCULATE (
SUM ( Sales[Amount] ),
REMOVEFILTERS ( Sales[Color] )
)
)
数据源:
让我们再次关注蓝色集群。1300从哪里来?为了理解它,我们首先构建集群,注意到集群中现在只有两列:Sales[Color] 和 Sales[Quantity]。通过删除 Sales[Color] 上的筛选器,唯一剩下的筛选器是 Sales[Quantity] 上的两个值:3 和 4。因此,结果是具有数量(3和4)的所有列的 Sales[Amount] 的总和:
CALCULATE (
CALCULATE (
SUM ( Sales[Amount] ),
REMOVEFILTERS ( Sales[Color] )
),
FILTER (
ALL ( Sales[Color], Sales[Quantity] ),
( Sales[Color], Sales[Quantity] )
IN {
( "Blue", 3 ),
( "Blue", 4 )
}
)
)
总结:在利用Summarize函数进行新建列计算时,一定要注意它的筛选器并不仅仅是集群标头,它包含表上的所有列。
SUMMARIZE 的另一个方面是它是 DAX 中唯一同时创建行上下文和筛选上下文的函数。在评估新列期间,SUMMARIZE 对集群进行迭代并生成:
这种独特的行为给本来就很复杂的函数增加了一些混乱。实际上,以下查询会生成一个颜色列表,在所有列中复制相同的颜色名称:
Product_Color =
SUMMARIZE (
Sales1,
'Product'[Color],
"Color 1", Product[Color],
"Color 2", SELECTEDVALUE ( Product[Color] ),
"Color 3", VALUES ( Product[Color] )
)
颜色 1 基于行上下文,其中仅包含簇标题。颜色 2 和颜色 3 依赖于筛选上下文。
就其本身而言,这种行为并不坏。它只会造成一些混乱,因为当您在 SUMMARIZE 中使用 CALCULATE 时,您不仅会更改筛选上下文,还会对簇标题中的所有列调用上下文转换。
先说结论,建议不要使用SUMMARIZE函数来增加扩展列,而是使用ADDCOLUMNS 和 SUMMARIZE的组合。不推荐使用的原因是:新增列的计算同时处于行上下文(row context)和筛选上下文(filter context)中,这会使得结果很复杂。
ADDCOLUMNS 和 SUMMARIZE组合的使用方式如下:
ADDCOLUMNS(
SUMMARIZE( <table>, <group by column> ),
<column_name>, CALCULATE( <expression> )
)
注意,当<expression>包含聚合函数时,CALCULATE函数是必须的。这是因为ADDCOLUMNS函数根据SUMMARIZE的结果产生一个行上下文(row context),这个行上下文不会自动转换为筛选上下文(filter context),而CALCULATE(<expression>) 会把行上下文转换为等价的筛选上下文。
统计每个国家对应的行数:
ADDCOLUMNS (
SUMMARIZE (
Store,
Store[Country]
),
"Stores", CALCULATE(COUNTROWS( Store ))
)
参考资料:
[1] 理解 SUMMARIZE(https://www.powerbigeek.com/understanding-summarize/)
[2] PowerBI中Summarize的使用方法(https://zhuanlan.zhihu.com/p/358729876)
[3] SUMMARIZE函数(https://learn.microsoft.com/zh-cn/dax/summarize-function-dax)
[4] DAX:SUMMARIZE的秘密(https://blog.csdn.net/upluck/article/details/128240035)
[5] All the secrets of SUMMARIZE(https://www.sqlbi.com/articles/all-the-secrets-of-summarize/)
[6] 深入理解Power BI中这个强大的函数:SUMMARIZE_参数(https://www.sohu.com/a/324873514_584557)