0

0

如何编写递归SQL存储过程_利用存储过程自身调用实现层级

P粉602998670

P粉602998670

发布时间:2026-03-19 14:34:02

|

188人浏览过

|

来源于php中文网

原创

SQL Server存储过程支持递归调用但需逻辑终止条件,MAXRECURSION仅限CTE;PostgreSQL和MySQL 8.0+推荐用WITH RECURSIVE而非存储过程递归,关键要设终止条件并为parent_id建索引。

如何编写递归sql存储过程_利用存储过程自身调用实现层级

SQL Server 里 sp_executesql 不能直接递归调用存储过程?

不能。SQL Server 的存储过程本身支持自调用(即递归调用),但必须显式启用,且默认被禁用。关键不是语法写错,而是数据库级配置没开——RECURSIVE_TRIGGERS 选项只影响触发器,和存储过程无关;真正卡住的是 MAXRECURSION 限制和会话级设置。

  • MAXRECURSION 是查询提示(OPTION (MAXRECURSION n)),只对 CTE 有效,对存储过程调用无约束
  • 存储过程递归靠的是「在过程体内执行自身」,例如 EXEC @proc_nameEXEC dbo.GetParent @id
  • 默认允许,但若嵌套过深(通常 > 32 层),SQL Server 会抛出错误:The statement terminated. The maximum recursion 100 has been exhausted before statement completion. —— 这其实是 CTE 报的,不是存储过程本体
  • 真正要防的是栈溢出或无限循环,得靠逻辑终止条件,不是靠开关

PostgreSQL 中用 WITH RECURSIVE 替代存储过程递归更稳妥

PostgreSQL 没有“存储过程递归调用自身”的惯用模式,因为函数(FUNCTION)支持 RETURNS TABLE + WITH RECURSIVE,一条 SQL 就能拉出整棵树。硬写成过程反而绕路、难调试、还容易漏 RETURN

  • 典型场景:查某个部门的所有下级部门(含子孙)
  • 错误做法:写个 get_sub_depts(id) 函数,在里面 SELECT ... FROM get_sub_depts(child_id) —— 不合法,PG 不允许函数内直接递归调用自身(除非标记 VOLATILE 且手动管理栈)
  • 正确做法:用 WITH RECURSIVE dept_tree AS ( SELECT id, parent_id FROM departments WHERE id = $1 UNION ALL SELECT d.id, d.parent_id FROM departments d INNER JOIN dept_tree t ON d.parent_id = t.id ) SELECT * FROM dept_tree;
  • 性能上,CTE 递归是优化器可感知的,而过程递归每次都要解析、计划、执行,开销翻倍

MySQL 8.0+ 支持递归 CTE,但存储过程仍不推荐用于层级遍历

MySQL 8.0 加了 WITH RECURSIVE,但它的存储过程语法不支持动态递归调用(比如不能在 CALL 里拼接过程名再执行)。强行用临时表 + 循环模拟递归,代码冗长、事务风险高、并发时容易锁表。

阶跃星辰开放平台
阶跃星辰开放平台

阶跃星辰旗下开放平台,提供文本大模型、多模态大模型、繁星计划

下载
  • 常见错误现象:写了个 proc_get_ancestors,里面用 WHILE 查父节点再 INSERT INTO temp,结果发现同一节点被重复插入,或漏掉某层
  • 根本原因:没处理好边界(如根节点 parent_id IS NULL 未退出)、没加 UNIQUE 约束、或 temp 表没清空
  • 更稳的方案:直接用 CTE,例如 WITH RECURSIVE tree AS ( SELECT id, name, parent_id, 1 depth FROM categories WHERE id = ? UNION ALL SELECT c.id, c.name, c.parent_id, t.depth + 1 FROM categories c INNER JOIN tree t ON c.id = t.parent_id ) SELECT * FROM tree;
  • 兼容性注意:MySQL 5.7 及更早版本不支持 CTE,此时只能靠应用层迭代,别硬塞进存储过程

所有数据库都踩过的坑:递归没设终止条件 or 忘记索引

不管用哪种方式实现层级查询,两个点一漏就慢到超时、甚至把数据库拖挂:一个是逻辑上没封死循环路径,一个是物理上没给关联字段建索引。

  • 终止条件缺失:比如组织架构中出现 A → B → C → A 的环,CTE 会一直跑直到 MAXRECURSION 触顶;存储过程中没检查 @parent_id = @current_id 就继续调用,直接栈溢出
  • 索引遗漏:递归查询必走 parent_id → idid → parent_id 关联,但很多人只在 id 上建了主键,忘了给 parent_id 加索引,导致每次找子节点都是全表扫描
  • 额外提醒:SQL Server 的 CTE 递归默认最多 100 层(OPTION (MAXRECURSION 100)),设成 0 表示不限,但慎用——真遇到环,不限等于死循环

递归层级查询的本质不是“怎么调用自己”,而是“怎么安全地展开关系图”。选对工具(CTE > 存储过程)、封死环路、索引到位,剩下的就是数据的事了。

相关标签:

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

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
sqlserver和mysql区别
sqlserver和mysql区别

SQL Server和MySQL是两种广泛使用的关系型数据库管理系统。它们具有相似的功能和用途,但在某些方面存在一些显著的区别。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

543

2023.08.11

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

391

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2113

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

359

2023.08.31

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

259

2023.09.05

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

329

2023.10.09

数据库对象名无效怎么解决
数据库对象名无效怎么解决

数据库对象名无效解决办法:1、检查使用的对象名是否正确,确保没有拼写错误;2、检查数据库中是否已存在具有相同名称的对象,如果是,请更改对象名为一个不同的名称,然后重新创建;3、确保在连接数据库时使用了正确的用户名、密码和数据库名称;4、尝试重启数据库服务,然后再次尝试创建或使用对象;5、尝试更新驱动程序,然后再次尝试创建或使用对象。

420

2023.10.16

vb连接access数据库的方法
vb连接access数据库的方法

vb连接access数据库方法:1、使用ADO连接,首先导入System.Data.OleDb模块,然后定义一个连接字符串,接着创建一个OleDbConnection对象并使用Open() 方法打开连接;2、使用DAO连接,首先导入 Microsoft.Jet.OLEDB模块,然后定义一个连接字符串,接着创建一个JetConnection对象并使用Open()方法打开连接即可。

482

2023.10.16

Go Web框架Gin接口开发与中间件设计实践
Go Web框架Gin接口开发与中间件设计实践

本专题围绕 Go 在 Web 后端开发中的主流框架 Gin 展开,系统讲解高性能接口开发与中间件机制设计。内容涵盖路由分组、请求绑定、参数校验、统一响应封装、日志与鉴权中间件实现,以及接口限流与异常处理策略。通过实战项目案例,帮助开发者构建结构清晰、性能优良的 Go Web 服务体系,提升接口开发效率与系统可维护性。

7

2026.03.19

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号