GROUPBY 函数在语法上类似于 SUMMARIZE,但不提供 SUMMARIZE 生成的筛选上下文,也不提供可转换的行上下文。它的目的是通过 CURRENTGROUP 函数对分组后的每一行应用迭代器。
GROUPBY
GROUPBY ( <Table>, [<GroupBy_ColumnName>, ...], [<Name>, <Expression>], [ … ] )
创建一个包含指定列的分组表。
参数 | 属性 | 描述 |
---|---|---|
Table | 需要分组的表. | |
GroupBy_ColumnName | 可选 可重复 |
用于分组的列. |
Name | 可选 可重复 |
需要添加的列名. |
Expression | 可选 可重复 |
新列使用的表达式 |
示例用法
例如,你可以这样使用 GROUPBY 重写 SUMMARIZE 的第一个案例:
EVALUATE GROUPBY ( Sales, 'Date'[Calendar Year Number], "Quantity", SUMX ( CURRENTGROUP (), Sales[Quantity] ) )
你必须在表最外层的列表达式中使用 CURRENTGROUP,因此它不支持多层迭代函数嵌套。如果需要生成动态 DAX 查询,并且希望对另一个表表达式的结果应用特定的聚合,GROUPBY 函数会非常有用。
SUMMARIZE 的局限
除了性能方面的限制,SUMMARIZE 的另一个限制是不能聚合在其内部创建的派生列,例如,观察下面的查询,它看起来应该为每个产品类别返回相关子类别平均价格的最大值。
EVALUATE ADDCOLUMNS ( SUMMARIZE ( ADDCOLUMNS ( SUMMARIZE ( Product, 'Product Category'[Category], 'Product Subcategory'[Subcategory] ), "Average Price", CALCULATE ( AVERAGE ( Product[Unit Price] ) ) ), 'Product Category'[Category] ), "Max SubCat Avg Price", CALCULATE ( MAX ( [Average Price] ) ) )
但实际上查询会得到一个错误的 MAX ( [Average Price] )。原因是 DAX 只允许你聚合物理列,在聚合函数的本质一文中,我们介绍过类似的等价表达式:
MAX ( table[expression] ) = MAXX ( table, table[expression] )
如果你尝试聚合在查询中新建的列(例如本例中[Average Price]),引擎将无法找到相应的 MAXX 函数。为了获得正确的结果,你需要移除外层的 SUMMARIZE,直接对新建的列进行操作
EVALUATE ADDCOLUMNS ( VALUES ( 'Product Category'[Category] ), "Max SubCat Avg Price", CALCULATE ( MAXX ( ADDCOLUMNS ( SUMMARIZE ( Product, 'Product Category'[Category], 'Product Subcategory'[Subcategory] ), "Average Price", CALCULATE ( AVERAGE ( Product[Unit Price] ) ) ), [Average Price] ) ) )
但是这种写法的问题也很明显,代码冗长且计算效率不高。如果希望同时计算最低和最高平均价格,你需要复制两份 SUMMARIZE,这会导致更长的执行时间。
使用 GROUPBY 代替 SUMMARIZE
GROUPBY 可以用于聚合查询内部的派生列,避免 ADDCOLUMNS+SUMMARIZE 的冗长代码。但是,即使是 GROUPBY,我们也不能直接使用聚合函数,
GROUPBY 移除空值
GROUPBY 会自动从结果中删除 CURRENTGROUP 列中有空白结果的行。大多数情况这种行为都是符合直觉的:从结果中删除那些“空白行”,但是当你将使用 CURRENTGROUP 的计算与其他计算混合使用时,结果可能有些出乎意料。
例如,考虑以下查询,它只考虑平均价格高于 500 的产品,并在另一列中返回产品类别名称的字符串长度。
EVALUATE GROUPBY ( GROUPBY ( Product, 'Product Category'[Category], 'Product Subcategory'[Subcategory], "Average Price", AVERAGEX ( CURRENTGROUP (), Product[Unit Price] ) ), 'Product Category'[Category], "Max SubCat Avg Price", MAXX ( CURRENTGROUP (), IF ( [Average Price] > 500, [Average Price], BLANK () ) ), "Len", MAXX ( CURRENTGROUP (), LEN ( 'Product Category'[Category] ) ) )
尽管可以对所有产品类别统计名称的长度,但结果也只包括至少一个子类别平均价格高于 500 的产品类别。
值得注意的是,由于 GROUPBY 并非迭代函数,所以它既不生成行上下文,也不生成筛选上下文,所以计算字符串长度的代码不能直接引用当前行,仍然要套用 CURRENTGROUP ()函数。
如果需要显示价格列为空的值,只需将 MAXX 函数中的 BLANK()替换为 0
EVALUATE GROUPBY ( GROUPBY ( Product, 'Product Category'[Category], 'Product Subcategory'[Subcategory], "Average Price", AVERAGEX ( CURRENTGROUP (), Product[Unit Price] ) ), 'Product Category'[Category], "Max SubCat Avg Price", MAXX ( CURRENTGROUP (), IF ( [Average Price] > 500, [Average Price], 0) ), "Len", MAXX ( CURRENTGROUP (), LEN ( 'Product Category'[Category] ) ) )
高老师,我测了下GROUPBY,现在是不是也返回CURRENTGROUP()中的空白行了?
EVALUATE
GROUPBY (
Sales,
‘Date'[Calendar Year Number],
“Quantity”, SUMX ( CURRENTGROUP (), Sales[Quantity] )
)
老师,这里的sumx第一参数currentgroup(),实际是一张年份列的多行表吗? 还是指迭代年份列的行形成的单行单列表,谢谢
高飞老师你好,关于多次聚合的这个问题,我反复理解后还是有疑惑,麻烦你看看我的理解到底那里出了问题。
我上次提问以后,你告诉我说,其实三次聚合也可以,不过当时不太会写,也就不了了之了。现在感觉自己多嵌套一层也没有问题了,我就尝试写了下面的例子,三个表函数
table1和table2的区别是得到最内层的劳务队名称的不重复列的时候,一个是summarize,一个是values。table3和前两个的区别是summarize生成不重复列的时候,有3个列(包含了外面2层的聚合的两列)。我的疑问在于,在table1和table2中,最内层使用addcolumns+summarize(values)的时候得到了一个按照劳务队名称的最大值的虚拟表(此时的表只有2列,劳务队名称和对应的计价金额的最大值),为什么可以被第二层的备注和第三层的年份筛选(虚拟表已经没有这两列了)?我知道有行上下文的转化,不过因为虚拟表已经不是物理表了,按道理说已经无法继续按照其他条目聚合了才对,但是又可以返回正确结果,这个地方太绕了,我自己理解有限,想不通。table3的写法感觉更标准一些,最内层生成虚拟表的时候就考虑了外面2层的聚合条目,更好理解一些,外层的行上下文转化为筛选上下文时,虚拟表中有这一列,可以继续聚合很好理解,table1和table2就不知道怎么能够解释的通。
望老师解答。这个问题我提的次数实在太多,占用老师时间真是非常抱歉!
你好,我又有了新的疑惑。在了解到了groupby的等价形式以后,觉得这个等价形式好像还有优化的空间。我也是自己尝试出来的,我写了table1,table2,table3。table1是仿照文章里的写法,第一次聚合采用summarize,summarize参数后面的列需要把两次聚合的都写进去(年份和劳务队名称),但是我在table2中,我就在想为什么第一次聚合为什么要考虑第二次聚合的维度,在第一次的聚合里只写了第一次聚合的列(劳务队名称),外层不变,结果还是可以得到和table1一样的结果,table3直接把第一次聚合的summarize也替换成了values,也可以得到相同的结果。我就在想,这个示例中的规避两次使用summarize的写法是否可以等价成我table2里的写法,起码看起来更自然,第一次聚合不必考虑第二次聚合的维度?甚至为了简化,也可以写成两次都用values的写法?我只是测试了自己的一个表,并不知道是否能够完全替代,对于二次聚合我甚至还尝试了generate,generate的一个好处就是第一次聚合不用考虑第二次聚合的维度,结果其实table2这样的写法也不用?望解惑。
我在学习这个函数以后,有个疑惑,就是concatenatex属不属于带x的迭代函数这一类的,因为我尝试以下代码,发现报错,因为这个函数本来大家使用的不多,参考的资料也很少,所以我不明白是不是不能这么写,还是说我的用法出问题了:
VAR table4
GROUPBY (
table1,
[工班],[籍贯],
“平均年龄”, AVERAGEX ( CURRENTGROUP (), [年龄] ),
“籍贯”, CONCATENATEX ( CURRENTGROUP (), [籍贯],[籍贯], “,” )
)
但是像其他的平均、求和、计数,因为只有两个参数,就可以,特此提问。