后端层级(GROUP BY 进阶玩法:结合WITH ROLLUP实现多层次汇总,报表神器)

后端层级(GROUP BY 进阶玩法:结合WITH ROLLUP实现多层次汇总,报表神器)
GROUP BY 进阶玩法:结合WITH ROLLUP实现多层次汇总,报表神器

还在写多个SQL拼报表?试试这个一句SQL搞定所有层级汇总的“外挂”。

90%的人只会用GROUP BY做基础分组

日常工作中,我们经常需要对数据做分组统计:

后端层级(GROUP BY 进阶玩法:结合WITH ROLLUP实现多层次汇总,报表神器)

-- 按部门统计销售额SELECT     department,    SUM(sales_amount) AS total_salesFROM sales_recordsGROUP BY department;

输出:

department

total_sales

技术部

100000

市场部

150000

销售部

200000

但如果老板要的报表是这样的:

  • 每个部门的汇总
  • 所有部门的合计
  • 甚至每个部门下再分产品线的汇总

传统做法:写3个SQL,UNION ALL拼起来,或者导出Excel手工做。代码臃肿,性能差,维护成本高。

WITH ROLLUP:一句SQL搞定N层汇总

WITH ROLLUP 是GROUP BY的扩展,可以在分组结果后自动追加“小计”和“总计”行。

语法极简: 在GROUP BY后面加个 WITH ROLLUP 就行。

案例1:单维度汇总 + 总计

SELECT     department,    SUM(sales_amount) AS total_salesFROM sales_recordsGROUP BY department WITH ROLLUP;

输出:

department

total_sales


技术部

100000


市场部

150000


销售部

200000


NULL

450000

← 总计行,NULL代表全部部门的汇总

一行NULL代表总计,报表直接可用。

案例2:多维度钻取汇总

这是ROLLUP真正强大的地方。按 部门 -> 产品线 两级分组,自动生成:产品线小计 + 部门小计 + 总计。

SELECT     department,    product_line,    SUM(sales_amount) AS total_salesFROM sales_recordsGROUP BY department, product_line WITH ROLLUP;

输出:

department

product_line

total_sales


技术部

软件

60000


技术部

硬件

40000


技术部

NULL

100000

← 技术部小计

市场部

广告

80000


市场部

活动

70000


市场部

NULL

150000

← 市场部小计

销售部

直销

120000


销售部

渠道

80000


销售部

NULL

200000

← 销售部小计

NULL

NULL

450000

← 总计

注意输出顺序: ROLLUP按分组字段从右向左逐层汇总,NULL代表当前维度的小计。

案例3:三维度报表

业务场景:按 年份 -> 季度 -> 区域 统计销售额。

SELECT     YEAR(order_date) AS year,    QUARTER(order_date) AS quarter,    region,    SUM(amount) AS total_amount,    COUNT(*) AS order_countFROM ordersGROUP BY year, quarter, region WITH ROLLUP;

一句SQL生成:

  • 每个区域在该季度的销售额
  • 每个季度的小计(跨区域)
  • 每年的小计(跨季度)
  • 总计(跨年)

报表系统需要的就是这种多层级钻取数据。

让报表更优雅:处理NULL值

NULL在报表里不好看,用 COALESCE 或 IFNULL 替换成更友好的文字:

SELECT     COALESCE(department, '【全公司合计】') AS department,    COALESCE(product_line, '【部门小计】') AS product_line,    SUM(sales_amount) AS total_salesFROM sales_recordsGROUP BY department, product_line WITH ROLLUP;

输出:

department

product_line

total_sales

技术部

软件

60000

技术部

硬件

40000

技术部

【部门小计】

100000

市场部

广告

80000

市场部

活动

70000

市场部

【部门小计】

150000

销售部

直销

120000

销售部

渠道

80000

销售部

【部门小计】

200000

【全公司合计】

【部门小计】

450000

这样导出的Excel直接可读,无需二次处理。

性能优化:ROLLUP的执行计划

ROLLUP并不是多次GROUP BY再UNION,而是通过一次扫描完成所有层级的聚合。执行计划中会看到 Using temporary; Using filesort,这是正常的。

优化建议:

  1. 分组字段顺序:把基数低的字段放前面(如部门、年份),基数高的放后面(如产品、月份)
  2. 只SELECT必要字段
  3. 配合WHERE条件先过滤数据
-- 好的写法:先过滤,再汇总SELECT     department,    product_line,    SUM(sales_amount) AS total_salesFROM sales_recordsWHERE order_date >= '2024-01-01'  -- 先过滤GROUP BY department, product_line WITH ROLLUP;

避坑指南

  1. ROLLUP与ORDER BY的配合:ROLLUP产生的汇总行在结果集末尾,如果用了ORDER BY会打乱层级顺序。建议在应用层排序,或者用ORDER BY IS NULL, department这种方式让汇总行保持最后。
  2. MySQL版本:MySQL 5.5+支持,5.7+优化更佳。PG用GROUP BY ROLLUP(...),SQL Server用WITH ROLLUP,语法略有差异。
  3. 识别汇总行:通过分组字段是否为NULL判断当前行是哪一层汇总。多维度时,从右向左为NULL的字段就是汇总维度。

实际应用场景

  • BI报表导出:用户在前端勾选多层级,后端一句SQL返回带钻取的数据结构
  • 财务对账:按机构->部门->科目逐级汇总
  • 销售看板:按区域->城市->门店层级统计
  • 数据核对:快速验证明细和汇总是否一致(总计行和明细SUM对不上就是数据问题)

总结

WITH ROLLUP的威力在于:

  • 一句SQL替代多句SQL+UNION
  • 一次扫描完成多层级聚合,性能优于多次查询
  • 层级可扩展:GROUP BY几个字段就自动生成几层汇总

下次写报表统计时,别再用UNION ALL拼合计了。GROUP BY ... WITH ROLLUP,干净利落。

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有

相关阅读