【自记】SQL 中 GROUPING 和 GROUPING SETS 语句的案例说明

发布于:2025-09-12 ⋅ 阅读:(21) ⋅ 点赞:(0)

        我们用一个生活中的例子来理解,比如你开了家小超市,想统计「销售额」,但需要从多个角度看(比如按 “日期 + 商品”、“仅日期”、“仅商品”、“整体总销售额”)。

假设你的销售数据长这样(简化版):

日期 商品 销售额
2023-10-01 可乐 100
2023-10-01 薯片 80
2023-10-02 可乐 120
2023-10-02 薯片 90

你想同时得到 4 种统计结果:

  1. 按「日期 + 商品」统计(最明细的维度);
  2. 仅按「日期」统计(每天总销售额);
  3. 仅按「商品」统计(每种商品总销售额);
  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

需求

统计每个月的销量,同时按以下维度组合分析:

  1. 月份 + 地区 + 产品(最明细)
  2. 月份 + 地区(不区分产品)
  3. 月份 + 产品(不区分地区)
  4. 月份(不区分地区和产品)

要求结果中:

  • 保留「月份」作为固定显示的普通字段;
  • 「地区」和「产品」用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 处理的字段:核心区别

  1. 普通字段(如month

    • 必须出现在GROUP BY中(否则 SQL 会报错,因为非聚合字段必须参与分组)。
    • 其值是「固定分组维度」,在所有结果行中都显示实际值(如2023-092023-10),不会被替换为'ALL'
    • 作用:作为所有统计维度的 “基础锚点”(比如这里所有统计都基于 “月份” 展开)。
  2. GROUPING 处理的字段(如regionproduct

    • 不一定在所有分组组合中出现(由GROUPING SETS控制)。
    • 其值是「动态分组维度」:当参与当前分组时显示实际值(如华北手机),不参与时显示'ALL'(表示该维度被汇总)。
    • 作用:灵活切换不同维度的统计视角,同时用'ALL'清晰标记 “当前行是该维度的汇总结果”。

        简单说:普通字段是 “固定不变的分组锚点”,GROUPING 处理的字段是 “可开关的动态维度”,前者值固定,后者值随分组组合动态变化(实际值或汇总标记)。


网站公告

今日签到

点亮在社区的每一天
去签到