如何用递归 CTE 实现“路径去环”检测循环依赖
发布时间 - 2026-01-26 00:00:00 点击率:次递归CTE中的循环依赖指数据存在闭环(如A→B→C→A),导致无限递归;PostgreSQL用ARRAY+@>检测路径重复节点,SQL Server用带边界符的字符串+CHARINDEX查重,二者均需在WHERE中主动剪枝以避免报错。
什么是递归 CTE 中的循环依赖
递归 CTE 在查询层级关系(如组织架构、物料 BOM、权限继承)时,如果数据中存在 A→B→C→A 这类闭环,WITH RECURSIVE 默认会无限展开,PostgreSQL 报错 ERROR: infinite recursion detected,SQL Server 报 Maximum recursion exceeded,SQLite 则可能卡死或截断。这不是语法错,是数据逻辑冲突暴露出来的环。
PostgreSQL:用 ARRAY + @> 检测路径重复节点
核心思路是把每层递归的路径存成 ARRAY,在递归成员中检查当前节点是否已出现在历史路径里。PostgreSQL 的 @>(包含操作符)比 UNNEST + EXISTS 更快且可索引。
- 起始查询必须包含
ARRAY[origin_id]作为初始路径 - 递归部分用
path || child_id追加,而非拼字符串(避免类型不一致或空值中断) -
WHERE NOT path @> ARRAY[child_id]是关键守门条件,漏掉就进环 - 注意
child_id IS NOT NULL要显式判断,否则NULL参与@>会返回NULL,导致行被意外过滤
WITH RECURSIVE deps AS (
SELECT id AS origin_id, depends_on AS child_id, ARRAY[id] AS path
FROM modules WHERE depends_on IS NOT NULL
UNION ALL
SELECT d.origin_id, m.depends_on, d.path || m.id
FROM deps d
JOIN modules m ON d.child_id = m.id
WHERE m.depends_on IS NOT NULL
AND NOT d.path @> ARRAY[m.id]
)
SELECT * FROM deps;SQL Server:用 MAXRECURSION 配合 CHARINDEX 字符串路径
SQL Server 不支持数组类型,常用逗号分隔字符串记录路径,再用 CHARINDEX 查重。但要注意:直接用 ',' + path + ',' LIKE '%,' + child_id + ',%' 易误匹配(比如 '1' 会被 '11' 匹中),必须前后加逗号并确保格式统一。
- 起始路径写成
',' + CAST(id AS VARCHAR) + ',',不是
CAST(id AS VARCHAR) - 递归中用
path + CAST(child_id AS VARCHAR) + ','会漏前导逗号,应统一为path + CAST(child_id AS VARCHAR) + ','并在查重时补头尾 -
OPTION (MAXRECURSION 100)必须加,否则默认只跑 100 层,深层合法树也会被截断 - 若
child_id是NULL,CAST(NULL AS VARCHAR)得到NULL,整行消失,需提前WHERE depends_on IS NOT NULL
WITH deps AS (
SELECT id AS origin_id, depends_on AS child_id,
',' + CAST(id AS VARCHAR(10)) + ',' AS path
FROM modules WHERE depends_on IS NOT NULL
UNION ALL
SELECT d.origin_id, m.depends_on,
d.path + CAST(m.id AS VARCHAR(10)) + ','
FROM deps d
INNER JOIN modules m ON d.child_id = m.id
WHERE m.depends_on IS NOT NULL
AND CHARINDEX(',' + CAST(m.id AS VARCHAR(10)) + ',', d.path) = 0
)
SELECT * FROM deps OPTION (MAXRECURSION 0);
为什么不能只靠 MAXRECURSION 或超时机制
仅设 MAXRECURSION 0 或调大限制,只是让报错延后,并不解决环本身。真实场景中,一个环可能藏在某条分支下,其余分支正常;若不主动剪枝,整个 CTE 会因该分支失败而整体失败(SQL Server)或抛异常(PostgreSQL)。更危险的是,有些环只在特定参数组合下触发,上线后偶发崩溃,排查成本远高于写对路径检测逻辑。
真正要命的不是“有没有环”,而是“有没有在展开时立刻识别并跳过它”。数组查重和带边界符的字符串查重,都是为了在进入下一层前,把环扼杀在 WHERE 子句里——这步漏了,后面全是徒劳。
# 为什么
# sql
# 架构
# Array
# NULL
# Error
# 字符串
# 递归
# 循环
# 继承
# bom
# sqlite
# postgresql
# 报错
# 闭环
# 的是
# 都是
# 子句
# 也会
# 出现在
# 并在
# 这类
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
用yum安装MySQLdb模块的步骤方法
网站建设要注意的标准 促进网站用户好感度!
如何基于云服务器快速搭建网站及云盘系统?
Laravel怎么判断请求类型_Laravel Request isMethod用法
html5源代码发行怎么设置权限_访问权限控制方法与实践【指南】
微信小程序 HTTPS报错整理常见问题及解决方案
Python正则表达式进阶教程_复杂匹配与分组替换解析
如何快速辨别茅台真假?关键步骤解析
Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】
如何快速生成凡客建站的专业级图册?
python中快速进行多个字符替换的方法小结
Linux系统运维自动化项目教程_Ansible批量管理实战
用v-html解决Vue.js渲染中html标签不被解析的问题
Laravel storage目录权限问题_Laravel文件写入权限设置
三星网站视频制作教程下载,三星w23网页如何全屏?
EditPlus 正则表达式 实战(3)
Laravel如何部署到服务器_线上部署Laravel项目的完整流程与步骤
bootstrap日历插件datetimepicker使用方法
Python文件流缓冲机制_IO性能解析【教程】
微信小程序 五星评分(包括半颗星评分)实例代码
QQ浏览器网页版登录入口 个人中心在线进入
如何在IIS中新建站点并解决端口绑定冲突?
如何在Windows环境下新建FTP站点并设置权限?
Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】
弹幕视频网站制作教程下载,弹幕视频网站是什么意思?
php后缀怎么变mp4格式错误_修改扩展名提示格式不对怎么办【技巧】
在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?
,在苏州找工作,上哪个网站比较好?
Laravel怎么定时执行任务_Laravel任务调度器Schedule配置与Cron设置【教程】
Edge浏览器如何截图和滚动截图_微软Edge网页捕获功能使用教程【技巧】
Linux虚拟化技术教程_KVMQEMU虚拟机安装与调优
JavaScript实现Fly Bird小游戏
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
Laravel如何实现数据导出到CSV文件_Laravel原生流式输出大数据量CSV【方案】
怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?
教学论文网站制作软件有哪些,写论文用什么软件
?
Laravel如何处理CORS跨域问题_Laravel项目CORS配置与解决方案
如何在阿里云购买域名并搭建网站?
成都网站制作公司哪家好,四川省职工服务网是做什么用?
猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】
Python高阶函数应用_函数作为参数说明【指导】
通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】
如何利用DOS批处理实现定时关机操作详解
阿里云高弹*务器配置方案|支持分布式架构与多节点部署
Laravel怎么集成Log日志记录_Laravel单文件与每日日志配置及自定义通道【详解】
LinuxCD持续部署教程_自动发布与回滚机制
Laravel的契約(Contracts)是什么_深入理解Laravel Contracts与依赖倒置
智能起名网站制作软件有哪些,制作logo的软件?
Laravel如何连接多个数据库_Laravel多数据库连接配置与切换教程
如何挑选优质建站一级代理提升网站排名?


