超越单个 GROUP BY
在多个层级进行聚合
超越单个 GROUP BY 是 CoddyKit 上的免费 SQL Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 SQL Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 SQL Academy 课程共包含 4 节课。
单个 GROUP BY 的局限性
标准的 GROUP BY 子句可以让您在一个特定层级上聚合行,例如统计每个地区的销售总额。但如果您还想在同一个查询中获得每个产品类别的总额以及总计,该怎么办?
将查询重复三次并使用 UNION ALL 虽然可行,但既冗长又缓慢。SQL 提供了三个强大的扩展——GROUPING SETS、ROLLUP 和 CUBE——可以在一次扫描中优雅地解决这个问题。
SELECT region, SUM(amount) AS total_sales
FROM sales
GROUP BY region;设置示例表
在本课中,我们将使用一个简单的 sales 表,该表记录每笔销售的 区域、类别 和 金额。让我们创建并填充该表,这样接下来的每个查询都能结合上下文理解。
CREATE TABLE sales (
id SERIAL PRIMARY KEY,
region TEXT,
category TEXT,
amount NUMERIC
);
INSERT INTO sales (region, category, amount) VALUES
('North', 'Electronics', 1200),
('North', 'Clothing', 800),
('South', 'Electronics', 950),
('South', 'Clothing', 600),
('East', 'Electronics', 1100),
('East', 'Clothing', 750);什么是 GROUPING SETS?
GROUPING SETS 允许您在一个 GROUP BY 子句中定义多个相互独立的分组层级。列表中的每个集合都会生成自己的一组行,就像分别编写查询,再使用 UNION ALL 将它们合并一样。
语法如下:GROUP BY GROUPING SETS ( (col1, col2), (col1), () )。空集合 () 表示所有行的总计。
SELECT region, category, SUM(amount) AS total
FROM sales
GROUP BY GROUPING SETS (
(region, category),
(region),
()
);读取 GROUPING SETS 输出
运行 GROUPING SETS 查询时,不同分组层级的行会堆叠在一起。对于特定集合不包含的列,该行中会显示为 NULL。
例如,属于 (region) 集合的行会在 category 列中显示 NULL,这表示该总计涵盖该区域的所有类别。总计行(空集合)会在 region 和 category 两列中都显示 NULL。
SELECT
COALESCE(region, 'ALL REGIONS') AS region,
COALESCE(category, 'ALL CATEGORIES') AS category,
SUM(amount) AS total
FROM sales
GROUP BY GROUPING SETS (
(region, category),
(region),
()
)
ORDER BY region NULLS LAST, category NULLS LAST;介绍 ROLLUP
ROLLUP 是常见分组集合层级的简写。给定列 (A, B) 后,ROLLUP(A, B) 会自动生成以下集合:(A, B)、(A) 和 ()。
这非常适合处理层级数据,例如从年到月再到日,或从区域到类别。生成的集合数量始终为 n + 1,其中 n 是列的数量。
-- ROLLUP(region, category) is equivalent to:
-- GROUPING SETS ( (region, category), (region), () )
SELECT region, category, SUM(amount) AS total
FROM sales
GROUP BY ROLLUP(region, category)
ORDER BY region NULLS LAST, category NULLS LAST;三个层级的 ROLLUP
向 ROLLUP 添加第三列后,层级会再扩展一级。ROLLUP(A, B, C) 会生成四个集合:(A, B, C)、(A, B)、(A) 和 ()。
在下面的示例中,year 是最外层级,category 是粒度最细的层级。该查询会在层级的每一步生成小计,此外还会生成一行总计。
SELECT
year,
region,
category,
SUM(amount) AS total
FROM (
VALUES
(2024, 'North', 'Electronics', 1200),
(2024, 'North', 'Clothing', 800),
(2024, 'South', 'Electronics', 950),
(2025, 'North', 'Electronics', 1400),
(2025, 'South', 'Clothing', 700)
) AS t(year, region, category, amount)
GROUP BY ROLLUP(year, region, category)
ORDER BY year NULLS LAST, region NULLS LAST, category NULLS LAST;介绍 CUBE
CUBE 比 ROLLUP 更为全面。给定 n 列后,它会生成分组集合的 所有 2^n 种可能组合,其中包括总计。
对于 CUBE(region, category),生成的集合为:(region, category)、(region)、(category) 和 (),总共四个集合。当您希望查看各个方向上的横向总计,而不只是单一层级时,这会非常有用。
-- CUBE(region, category) produces:
-- GROUPING SETS ( (region,category), (region), (category), () )
SELECT region, category, SUM(amount) AS total
FROM sales
GROUP BY CUBE(region, category)
ORDER BY region NULLS LAST, category NULLS LAST;CUBE 与 ROLLUP——何时使用哪一个
请选择适合您数据维度自然层级的方式:
- 当列具有层级关系(例如从国家到城市再到门店)时使用 ROLLUP。小计只会沿一条路径逐级汇总。
- 当列是相互独立的维度(例如区域和类别),并且您希望查看所有可能的横向组合时,使用 CUBE。
- 当您需要完全控制分组方式,而 ROLLUP 和 CUBE 都无法满足确切要求时,使用 GROUPING SETS。
GROUPING() 函数
由于 NULL 既可能表示实际缺失值,也可能表示分组占位符,SQL 提供了 GROUPING() 函数。当某列属于超聚合(占位符)行时,它返回 1;当该列确实参与分组时,返回 0。
这样,您就可以区分真正的 NULL 区域值和涵盖所有区域的小计行。
SELECT
region,
category,
SUM(amount) AS total,
GROUPING(region) AS is_region_subtotal,
GROUPING(category) AS is_category_subtotal
FROM sales
GROUP BY CUBE(region, category)
ORDER BY region NULLS LAST, category NULLS LAST;使用 GROUPING() 标记行
一种常见做法是将 GROUPING() 与 CASE 表达式结合起来,用易于理解的标签替代原始的 NULL 值。这样,报表中的 ROLLUP 或 CUBE 查询输出会更易于阅读。
SELECT
CASE GROUPING(region)
WHEN 1 THEN 'Grand Total'
ELSE region
END AS region_label,
CASE GROUPING(category)
WHEN 1 THEN 'All Categories'
ELSE category
END AS category_label,
SUM(amount) AS total
FROM sales
GROUP BY ROLLUP(region, category)
ORDER BY GROUPING(region), region, GROUPING(category), category;将固定列与 ROLLUP 混用
您可以在同一个子句中将常规 GROUP BY 列与 ROLLUP 或 CUBE 混用。列在 ROLLUP(...) 外部列出时,会始终包含在每个分组集合中,永远不会被逐级汇总。
在下面的查询中,year 是固定的分组列,而 region 和 category 参与逐级汇总。因此,您会得到按年份划分的小计,而不是跨所有年份的总计。
SELECT
2024 AS year,
region,
category,
SUM(amount) AS total
FROM sales
GROUP BY 2024, ROLLUP(region, category)
ORDER BY region NULLS LAST, category NULLS LAST;快速检查
测试您对 GROUPING SETS、ROLLUP 和 CUBE 的理解。
回顾——超越单个 GROUP BY
在本课中,您学习了三个强大的 SQL 扩展,它们可以让您在单个查询中生成多个汇总层级:
- GROUPING SETS——完全手动控制;列出您确切需要的组合。
- ROLLUP——非常适合层级数据;从最详细的层级逐级汇总到总计。
- CUBE——生成所列维度的所有可能横向组合。
您还学会了使用 GROUPING() 区分真正的 NULL 值和超聚合占位行,以及使用 CASE GROUPING(...) 模式生成整洁、易读的报表输出。
每当您需要多维汇总时,这些工具都不可或缺——透视式报表、仪表板和数据仓库查询都高度依赖它们。
常见问题解答
「超越单个 GROUP BY」课时是免费的吗?
是的 — 「超越单个 GROUP BY」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 SQL Academy 课程的其余内容,请升级到 CoddyKit PRO。 SQL Academy 课程共包含 4 节课。
「超越单个 GROUP BY」这节课中我会学到什么?
在多个层级进行聚合 你通过在浏览器中直接运行的动手代码来练习 SQL Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 SQL Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 SQL Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「超越单个 GROUP BY」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 SQL Academy 课中编写并运行代码吗?
能。每节 SQL Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 超越单个 GROUP BY
- 使用 ROLLUP 计算小计
- 使用 CUBE 处理所有组合
- GROUPING SETS 详解