编程SQL 开发人员 / 后端开发人员

描述在 SQL 中实现复杂迭代计算的不同方法。何时应该使用递归公共表表达式(CTE),而何时更好地诉诸其他方法?

用 Hintsage AI 助手通过面试

答案。

在 SQL 中,有几种实现迭代/递归计算的方法:

  • 递归 CTE (WITH RECURSIVE) — 适用于遍历层次结构(树、图、链接链)等任务。
  • 游标(CURSOR) — 允许逐行处理数据,但对于大数据量的处理效率低于基于集合的操作。
  • 普通的基于集合的表达式 — 在大多数情况下首选(处理无迭代的行集合)。

何时使用递归 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 秒内获得了相同的结果。