必须用 with recursive,锚点须命中真实根节点,递归部分通过 inner join 关联 parent_id 与上层 id,并在 select 中动态累加金额;旧版本不支持该语法。

MySQL 8.0+ 怎么用 WITH RECURSIVE 算子节点总金额
直接上结论:必须用 WITH RECURSIVE,且递归查询需明确锚点(根节点)和递进关系(父子关联)。不支持递归的旧版本 MySQL(
典型结构是树形组织表,比如 orgs 表含 id、parent_id、amount 字段。要查每个节点及其所有下级的 amount 总和,不能只靠 GROUP BY 或自连接——层级不确定时会断层。
- 递归 CTE 必须包含两部分:初始查询(锚点,如指定某个
root_id或所有顶级节点WHERE parent_id IS NULL) + 递归查询(JOIN自身,条件是t.parent_id = r.id) - 别名必须一致:递归引用的表名在
FROM和UNION ALL右侧都得用同一个别名(如r) - 务必加终止条件,否则可能死循环;MySQL 会默认限制 1000 层,但最好在
WHERE中显式过滤掉无效parent_id
WITH RECURSIVE tree AS ( SELECT id, parent_id, amount, id as root_id FROM orgs WHERE parent_id IS NULL -- 锚点:顶级节点 UNION ALL SELECT o.id, o.parent_id, o.amount, t.root_id FROM orgs o INNER JOIN tree t ON o.parent_id = t.id ) SELECT root_id, SUM(amount) as total_amount FROM tree GROUP BY root_id;
PostgreSQL 的递归写法和 cycle 检测怎么加
PostgreSQL 同样用 WITH RECURSIVE,但语法更灵活,且原生支持 CYCLE 子句防环。如果你的数据可能有脏数据(比如 A→B→C→A),不加 cycle 检测会导致无限递归报错:ERROR: stack depth limit exceeded。
-
CYCLE后必须跟一个列名(如id),用于标记路径中是否重复出现该值 - 它会自动生成两个隐藏列:
is_cycle(布尔)和path(数组,记录遍历过的id序列) - 实际汇总时,建议先
WHERE NOT is_cycle过滤掉成环路径,再GROUP BY
WITH RECURSIVE tree(id, parent_id, amount, path, is_cycle) AS ( SELECT id, parent_id, amount, ARRAY[id], FALSE FROM orgs WHERE parent_id IS NULL UNION ALL SELECT o.id, o.parent_id, o.amount, t.path || o.id, o.id = ANY(t.path) FROM orgs o, tree t WHERE o.parent_id = t.id AND NOT t.is_cycle ) SELECT t.id as node_id, SUM(o.amount) as total_downstream FROM tree t LEFT JOIN orgs o ON o.id = t.id OR o.parent_id = t.id GROUP BY t.id;
SQL Server 的 CTE 递归为什么常出 “maximum recursion exceeded” 错误
SQL Server 默认递归深度上限是 100 层,而很多组织架构或 BOM 表层级远超此数,一跑就报 Msg 530, Level 16, State 1, Line X: The statement terminated. The maximum recursion 100 has been exhausted before statement completion.。
- 必须在语句末尾加
OPTION (MAXRECURSION n),n设为 0 表示无限制(慎用,确保数据无环) - 如果只是想查某一层级下的汇总(比如只到第 3 级子节点),可在递归 CTE 中加一个
level计数器,并在WHERE level 控制 - 注意:SQL Server 的递归部分不能用
OUTER JOIN,只能用INNER JOIN,否则报语法错误
WITH tree AS ( SELECT id, parent_id, amount, 1 as level FROM orgs WHERE parent_id IS NULL UNION ALL SELECT o.id, o.parent_id, o.amount, t.level + 1 FROM orgs o INNER JOIN tree t ON o.parent_id = t.id WHERE t.level <h3>没有递归支持的老版本数据库怎么办</h3><p>MySQL 5.7、SQLite、早期 Oracle 都不支持标准递归 CTE,硬写多层 <code>LEFT JOIN</code> 不仅难维护,还必然漏掉深层节点。真实项目里最可行的是「应用层收口」:用一次查询拉出全量树结构,再由程序做 DFS/BFS 汇总。</p>
- 一次性查出全部节点:
SELECT id, parent_id, amount FROM orgs ORDER BY parent_id, id - 用哈希表建父子映射,再对每个根节点调用递归函数累加(Python 示例:用
defaultdict(list)存 children,然后def dfs(n): return amount[n] + sum(dfs(c) for c in children[n])) - 如果必须纯 SQL,可考虑生成固定深度视图(如
v_level1、v_level2),但超过 4 层就失去实用性
真正棘手的不是语法,而是“谁来定义根节点”——业务上是否允许任意节点作为起点?汇总口径是否包含自己?这些逻辑一旦写死在 SQL 里,后续改起来比换数据库还麻烦。










