如何在SQL中实现跨多层级部门的分组递归数据向上汇总?

大静大大_1010

大静大大_1010

2026-11-16

379人浏览

原创

WITH RECURSIVE 可实现从子部门向上递归聚合至根部门:先用递归CTE展开每个业务记录到根节点的完整路径(初始查sales关联部门,递归JOIN parent_id),再在外层按目标部门ID分组SUM(amount);需防环、建parent_id索引,并避免在递归内聚合。

如何在sql中实现跨多层级部门的分组递归数据向上汇总?

用 WITH RECURSIVE 从子部门向上聚合到根部门

SQL 本身不支持自动跨层级向上求和,必须显式构造递归路径。核心是先用 WITH RECURSIVE 把每个节点到根节点的完整路径展开,再按根部门(或任意上级)分组聚合。别指望 GROUP BY department_id 直接搞定——它只认当前行的 department_id,不会自动把子部门数据“提上去”。

典型结构:一张部门表 departments(含 id、name、parent_id),一张业务数据表 sales(含 dept_id、amount)。要算每个部门及其所有下级的销售总和,就得反向递归:从叶子节点出发,逐级 JOIN 父节点,直到 parent_id IS NULL。

  • 递归 CTE 的初始查询必须选中所有“末端”业务记录(即 sales 表的每一行),并关联其直属部门信息
  • 递归部分用 JOIN departments ON t.parent_id = d.id 向上跳一级,同时保留原始 sales.amount 和当前 d.id(即新上级)
  • 最终 SELECT 按 d.id(代表汇总目标部门)分组,SUM(amount) 即为该部门含全部下级的总额

MySQL 8.0+ 和 PostgreSQL 的语法差异点

MySQL 要求递归查询中不能出现聚合函数或 DISTINCT,所以必须在递归 CTE 内只做路径展开,聚合必须放在外层查询;PostgreSQL 允许在递归分支里加 GROUP BY,但实际没必要——容易混淆层级归属。

关键区别在初始查询字段顺序和递归引用方式:

  • MySQL:递归 CTE 的 SELECT 列数、类型、顺序必须严格一致,UNION ALL 左右两边都要有 dept_id(当前部门)、root_dept_id(最终要汇总到的部门)、amount
  • PostgreSQL:可直接用 SELECT ... FROM departments d JOIN RECURSIVE cte ON d.id = cte.parent_id,更贴近直觉,但需注意 MAX_RECURSION_DEPTH 默认值(可通过 max_recursion_depth 参数调)
  • 两者都不支持在递归部分对 amount 做累计(如 running_sum),因为会破坏集合语义——必须靠外层 SUM() 实现汇总

常见错误:循环引用导致 “Maximum recursion depth exceeded”

部门表里若存在 id=5, parent_id=5 或 id=3 → 4 → 3 这类自环/环状引用,递归会无限进行下去。数据库会在达到深度限制时报错,例如 MySQL 的 ERROR 3636 (HY000): Recursive query aborted after 1001 iterations。

  • 上线前务必检查 departments 表:运行 SELECT * FROM departments WHERE id = parent_id OR parent_id IN (SELECT id FROM departments WHERE parent_id = id) 找出自环
  • 在递归 CTE 中加入防环字段(如数组或字符串路径),PostgreSQL 可用 NOT d.id = ANY(path),MySQL 只能靠应用层预处理或加 LIMIT 应急
  • 别依赖 parent_id IS NULL 作为唯一终止条件——有些系统用 0 或 -1 表示根,得统一清理

性能陷阱:没加索引会让递归变全表扫描

递归过程本质是多次嵌套 JOIN,每次都要根据 parent_id 查找父节点。如果 departments(parent_id) 没索引,单次递归可能触发全表扫描,N 层递归就是 N × 表行数,1000 行部门表 + 5 层深度就可能扫几万行。

  • 必须确保 departments.parent_id 有 B-tree 索引(MySQL/PG 都适用)
  • 如果经常按“某部门及其下级”查数据,可额外建覆盖索引:CREATE INDEX idx_dept_parent_id_id ON departments(parent_id, id),让 JOIN 更快命中
  • 数据量大时(比如部门超 1w),考虑物化路径(path 字段存 “1/5/23/”)替代递归,虽然写入慢,但读取极快且无深度限制

递归不是银弹——层级超过 20 层或部门关系频繁变动时,CTE 的维护成本和不可预测性会上升。真正稳定的方案往往是在业务写入时同步更新一个 dept_summary 汇总表,而不是每次都现场算。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

5023

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

1031

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1329

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

7101

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3743

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

7100

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

9921

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1350

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1172

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 134.3万人学习