在 SQL 中,有几种实现迭代/递归计算的方法:
WITH RECURSIVE) — 适用于遍历层次结构(树、图、链接链)等任务。何时使用递归 CTE:
示例:
WITH RECURSIVE tree AS ( SELECT id, parent_id, name, 1 AS level FROM categories WHERE parent_id IS NULL UNION ALL SELECT c.id, c.parent_id, c.name, t.level + 1 FROM categories c JOIN tree t ON c.parent_id = t.id ) SELECT * FROM tree;
如果递归 CTE 无法解决问题或者数据过于庞大,则使用其他机制(例如,外部处理、临时表或 SQL 之外的专门算法)。
递归 CTE 是否可能导致无限执行?如何防止这种情况?
答案:
是的,如果图中存在循环(例如,parent_id 再次引用后代),递归 CTE 可能会进入死循环。大多数数据库管理系统支持限制递归的最大层数(MS SQL 的 MAXRECURSION 或类似选项)。良好的做法是始终限制递归深度。
-- MS SQL Server OPTION (MAXRECURSION 100)
故事
在一个金融项目中,通过递归 CTE 计算关联账户(parent-child)的最终余额。 一位用户在结构中创建了一个循环(父节点成为自己的后代)。查询开始无限执行,给服务器带来负担。添加了循环检查和
MAXRECURSION,问题得到解决。
故事
在任务管理系统中,使用游标处理依赖任务的状态。 迁移到递归 CTE 后,忘记优化 parent_id 的索引 — 性能下降了 5 倍,因为每一步都进行了高成本的连接操作。
故事
使用游标遍历类别树的查询正确工作,但处理 100,000 行耗时 7 分钟。 将其重写为被认为“慢”的递归 CTE — 通过集成引擎优化在 5 秒内获得了相同的结果。