????????我們用一個生活中的例子來理解,比如你開了家小超市,想統計「銷售額」,但需要從多個角度看(比如按 “日期 + 商品”、“僅日期”、“僅商品”、“整體總銷售額”)。
假設你的銷售數據長這樣(簡化版):
日期 | 商品 | 銷售額 |
---|---|---|
2023-10-01 | 可樂 | 100 |
2023-10-01 | 薯片 | 80 |
2023-10-02 | 可樂 | 120 |
2023-10-02 | 薯片 | 90 |
你想同時得到 4 種統計結果:
- 按「日期 + 商品」統計(最明細的維度);
- 僅按「日期」統計(每天總銷售額);
- 僅按「商品」統計(每種商品總銷售額);
- 不按任何維度(整體總銷售額)。
不用 GROUPING SETS 的話,你需要寫 4 個查詢,再合并:
-- 1. 日期+商品
SELECT 日期, 商品, SUM(銷售額) FROM 銷售表 GROUP BY 日期, 商品
UNION ALL
-- 2. 僅日期
SELECT 日期, 'ALL' 商品, SUM(銷售額) FROM 銷售表 GROUP BY 日期
UNION ALL
-- 3. 僅商品
SELECT 'ALL' 日期, 商品, SUM(銷售額) FROM 銷售表 GROUP BY 商品
UNION ALL
-- 4. 整體匯總
SELECT 'ALL' 日期, 'ALL' 商品, SUM(銷售額) FROM 銷售表
用 GROUPING SETS 的話,1 條查詢搞定:
SELECT -- 用GROUPING判斷列是否參與分組,不參與就顯示'ALL'IF(GROUPING(日期) = 0, 日期, 'ALL') AS 日期,IF(GROUPING(商品) = 0, 商品, 'ALL') AS 商品,SUM(銷售額) AS 總銷售額
FROM 銷售表
-- 一次性指定所有要統計的維度組合
GROUP BY 日期, 商品
GROUPING SETS ((日期, 商品), -- 對應需求1:日期+商品(日期), -- 對應需求2:僅日期(商品), -- 對應需求3:僅商品() -- 對應需求4:不分組(整體匯總)
)
最終結果長這樣:
日期 | 商品 | 總銷售額 | |
---|---|---|---|
2023-10-01 | 可樂 | 100 | -- 日期 + 商品維度 |
2023-10-01 | 薯片 | 80 | -- 日期 + 商品維度 |
2023-10-02 | 可樂 | 120 | -- 日期 + 商品維度 |
2023-10-02 | 薯片 | 90 | -- 日期 + 商品維度 |
2023-10-01 | ALL | 180 | -- 僅日期維度(100+80) |
2023-10-02 | ALL | 210 | -- 僅日期維度(120+90) |
ALL | 可樂 | 220 | -- 僅商品維度(100+120) |
ALL | 薯片 | 170 | -- 僅商品維度(80+90) |
ALL | ALL | 390 | -- 整體匯總(180+210 或 220+170) |
核心點總結:
GROUPING SETS(...)
:括號里寫多個 “維度組合”,一次查詢得到所有組合的統計結果(代替多次GROUP BY
+UNION ALL
)。GROUPING(列名)
:判斷這一列是否在當前行的 “維度組合” 中。如果在(參與了分組),返回 0,顯示實際值;如果不在(是匯總行),返回 1,用 'ALL' 標記,方便區分。
————————————
????????我們用一個更貼近實際業務的例子來說明:假設你有一張「訂單表」,需要統計不同維度的銷量,同時保留一個固定的分組字段(比如「月份」),并穿插普通查詢字段和GROUPING
處理的字段,看看它們的區別。
場景設定
訂單表orders
結構(簡化):
月份(month) | 地區(region) | 產品(product) | 銷量(sales) |
---|---|---|---|
2023-09 | 華北 | 手機 | 100 |
2023-09 | 華北 | 電腦 | 50 |
2023-09 | 華南 | 手機 | 80 |
2023-10 | 華北 | 手機 | 120 |
2023-10 | 華南 | 電腦 | 60 |
需求
統計每個月的銷量,同時按以下維度組合分析:
- 月份 + 地區 + 產品(最明細)
- 月份 + 地區(不區分產品)
- 月份 + 產品(不區分地區)
- 月份(不區分地區和產品)
要求結果中:
- 保留「月份」作為固定顯示的普通字段;
- 「地區」和「產品」用
GROUPING
處理,不參與分組時顯示'ALL'
; - 計算總銷量。
SQL 查詢(包含普通字段和 GROUPING 字段)
SELECT -- 普通字段:月份(始終在GROUP BY中,直接顯示實際值)month,-- GROUPING處理的字段:地區(是否參與分組動態顯示)IF(GROUPING(region) = 0, region, 'ALL') AS region,-- GROUPING處理的字段:產品(是否參與分組動態顯示)IF(GROUPING(product) = 0, product, 'ALL') AS product,-- 聚合字段:總銷量SUM(sales) AS total_sales
FROM orders
-- 固定按月份分組,同時用GROUPING SETS定義地區和產品的組合
GROUP BY month,
GROUPING SETS ((region, product), -- 組合1:月份+地區+產品(region), -- 組合2:月份+地區(無產品)(product), -- 組合3:月份+產品(無地區)() -- 組合4:僅月份(無地區和產品)
)
ORDER BY month, region, product;
查詢結果
month | region | product | total_sales | |
---|---|---|---|---|
2023-09 | 華北 | 手機 | 100 | -- 組合 1:月份 + 地區 + 產品 |
2023-09 | 華北 | 電腦 | 50 | -- 組合 1:月份 + 地區 + 產品 |
2023-09 | 華北 | ALL | 150 | -- 組合 2:月份 + 地區(匯總該地區所有產品) |
2023-09 | 華南 | 手機 | 80 | -- 組合 1:月份 + 地區 + 產品 |
2023-09 | 華南 | ALL | 80 | -- 組合 2:月份 + 地區(匯總該地區所有產品) |
2023-09 | ALL | 手機 | 180 | -- 組合 3:月份 + 產品(匯總所有地區該產品) |
2023-09 | ALL | 電腦 | 50 | -- 組合 3:月份 + 產品(匯總所有地區該產品) |
2023-09 | ALL | ALL | 230 | -- 組合 4:僅月份(匯總該月所有銷量) |
2023-10 | 華北 | 手機 | 120 | -- 組合 1:月份 + 地區 + 產品 |
2023-10 | 華北 | ALL | 120 | -- 組合 2:月份 + 地區(匯總該地區所有產品) |
2023-10 | 華南 | 電腦 | 60 | -- 組合 1:月份 + 地區 + 產品 |
2023-10 | 華南 | ALL | 60 | -- 組合 2:月份 + 地區(匯總該地區所有產品) |
2023-10 | ALL | 手機 | 120 | -- 組合 3:月份 + 產品(匯總所有地區該產品) |
2023-10 | ALL | 電腦 | 60 | -- 組合 3:月份 + 產品(匯總所有地區該產品) |
2023-10 | ALL | ALL | 180 | -- 組合 4:僅月份(匯總該月所有銷量) |
普通字段 vs GROUPING 處理的字段:核心區別
普通字段(如
month
):- 必須出現在
GROUP BY
中(否則 SQL 會報錯,因為非聚合字段必須參與分組)。 - 其值是「固定分組維度」,在所有結果行中都顯示實際值(如
2023-09
、2023-10
),不會被替換為'ALL'
。 - 作用:作為所有統計維度的 “基礎錨點”(比如這里所有統計都基于 “月份” 展開)。
- 必須出現在
GROUPING 處理的字段(如
region
、product
):- 不一定在所有分組組合中出現(由
GROUPING SETS
控制)。 - 其值是「動態分組維度」:當參與當前分組時顯示實際值(如
華北
、手機
),不參與時顯示'ALL'
(表示該維度被匯總)。 - 作用:靈活切換不同維度的統計視角,同時用
'ALL'
清晰標記 “當前行是該維度的匯總結果”。
- 不一定在所有分組組合中出現(由
????????簡單說:普通字段是 “固定不變的分組錨點”,GROUPING 處理的字段是 “可開關的動態維度”,前者值固定,后者值隨分組組合動態變化(實際值或匯總標記)。