PostgreSQL 17的GROUPING SETS说明,从FROM和WHERE选出的数据,会按每一个指定的分组集分别分组,并对各组计算聚合,再返回结果。一个分组集可以包含零个或多个列或表达式。多个分组集因此不是把所有分组条件同时压在一份组里,而是分别形成结果;读到一行聚合结果时,还需要知道它由哪一个分组集产生。

空分组集也有明确含义:所有行聚合成单一组,即使没有输入行,这个组仍会输出。另一个与空值相关的规则发生在结果列上。某个分组集没有包含的分组列或表达式,在它产生的结果行中以NULL替代。这里的空位首先说明该行的分组范围没有使用这一列,不能据此直接断定原数据的这一列全部缺失。空分组集与结果补位空值,一个描述分组成员,一个描述输出位置。

ROLLUP与CUBE是两种展开分组集的简写。ROLLUP保留给定表达式清单及其所有前缀,包括空清单。若清单依次为甲、乙、丙,它包含三者、甲乙、甲,以及空组;单独乙、单独丙不是这个前缀序列。CUBE则包含全部子集,同样三个独立成员会形成八种组合,其中也包含甲丙、乙丙等不属于上述前缀的组合。清单相同,两种展开规则保留的组合仍不同。

展开的单位还可能是一组表达式。文档把括号中的子清单视为一个整体,所以两对子清单组成CUBE时,展开结果为两对全部、一整对、另一整对,以及空组。它不在这一层继续把每一对拆成两个可以独立选择的成员。因而判断“有哪些子集”之前,先要识别参与组合的单位是单个表达式,还是被括号联合起来的子清单。

多个分组项放在同一个分组说明中时,最终分组集是各项的笛卡尔积。原文列出的一个普通项会出现在每一种组合中,另外各项再分别贡献自己的组合选择。两个ROLLUP项也可能通过不同组合途径产生同一个分组集。此时重复发生在分组集合的清单中,尚不是根据聚合结果各列的值判断两行是否相同。

GROUP BY上的DISTINCT针对的正是这种重复分组集。它与SELECT DISTINCT对最终结果行去重不同:先让每个分组集只出现一次,最后输出的行仍可能相同。文档特别指出,原列里已有的NULL,可能与没有使用该分组列时补入的NULL,在显示值上无法区分。于是,集合身份不同并不保证输出值不同,清除重复集合也就不能替代清除最终重复行。

原文还有一个符号层级注记:表达式顶层的括号清单,在GROUP BY中被当作表达式列表,而不是通常意义的行构造对象。这项说明与子清单整体参与组合放在一起,使相似的括号写法保留所在位置与用途。本文只解释17版文档中这些静态关系,未执行SQL或评价任何真实查询的性能。

信息来源

本文基于上述公开资料整理,未使用来源页面的图片、视频或嵌入媒体。