ROLLUP 是 GROUP BY 的擴充功能,能在單一查詢中同時產生明細、各層級小計、總計,極大簡化報表 SQL。本文以 EMP 表為例,完整展示 ROLLUP、CUBE、GROUPING SETS、GROUPING/GROUPING_ID 識別函數的實戰用法。
SELECT job, deptno, SUM(sal) AS total_sal
FROM emp
GROUP BY ROLLUP (job, deptno);
DEPTNO = NULLJOB = NULL, DEPTNO = NULL| 子句 | 彙總層級 | 適用場景 |
|---|---|---|
ROLLUP (a,b,c) | (a,b,c) → (a,b) → (a) → () | 層級式鑽取(年→季→月→日、國→省→市) |
CUBE (a,b,c) | 所有 2^n 組合 | 多維度交叉分析(全排列小計) |
GROUPING SETS ((a,b),(a),()) | 自訂組合集合 | 只需特定小計,效能最優 |
SELECT job, deptno, SUM(sal)
FROM emp
GROUP BY CUBE (job, deptno);
-- 產生:(job,deptno)、(job)、(deptno)、() 四組彙總
SELECT job, deptno, SUM(sal)
FROM emp
GROUP BY GROUPING SETS ((job, deptno), (job), ());
-- 僅產生明細、job 小計、總計;不產生 deptno 小計
結果集中 NULL 可能代表「真實資料為 NULL」或「彙總層級」。用識別函數區分:
SELECT job, deptno, SUM(sal) AS total_sal,
GROUPING(job) AS is_job_subtotal,
GROUPING(deptno) AS is_dept_subtotal,
GROUPING_ID(job, deptno) AS grouping_id
FROM emp
GROUP BY ROLLUP (job, deptno);
GROUPING(col):該列為彙總產生的 NULL 回傳 1,否則 0。GROUPING_ID(col1, col2...):將各 GROUPING 位元組合為整數(如 0=明細、1=deptno 小計、2=job 小計、3=總計)。SELECT DECODE(GROUPING(job), 1, '【所有職稱】', job) AS job,
DECODE(GROUPING(deptno), 1, '【所有部門】', TO_CHAR(deptno)) AS deptno,
SUM(sal) AS total_sal
FROM emp
GROUP BY ROLLUP (job, deptno)
ORDER BY GROUPING_ID(job, deptno), job, deptno;
SELECT region, product_category, month,
SUM(amount) AS total_amt,
GROUPING_ID(region, product_category, month) AS gid
FROM sales_fact
GROUP BY ROLLUP (region, product_category, month)
HAVING GROUPING_ID(region, product_category, month) IN (0, 1, 3, 7);
-- 0=明細, 1=月小計, 3=品類+月小計, 7=總計
-- 排除 region 小計 (gid=4) 和品類小計 (gid=6)
ROLLUP/CUBE 會增加排序/雜湊彙總開銷,大資料量建議配合 PARALLEL。GROUPING SETS 比 CUBE 效能好,只計算需要的組合。GROUPING/GROUPING_ID 可用於 HAVING、ORDER BY、應用層格式化。APPROX_* 近似彙總函數(APPROX_COUNT_DISTINCT 等)可配合 GROUPING SETS 快速估算。-- MySQL 8.0+ / PostgreSQL 9.5+ 支援標準 GROUPING SETS, ROLLUP, CUBE
SELECT job, deptno, SUM(sal)
FROM emp
GROUP BY ROLLUP (job, deptno);
-- GROUPING() 函數同樣支援
善用 ROLLUP、CUBE、GROUPING SETS 將多層級彙總下推至資料庫層,不僅 SQL 簡潔、網路傳輸減少,更能發揮資料庫平行彙總優勢,是報表開發的必備利器。