oracle 11gr2 不支持 with recursive,仅支持 connect by 递归;必须为 parent_id 列建索引,正确使用 prior 和 level,避免动态拼接,推荐封装为视图调用。
with recursive 在 oracle 11gr2 中不可用 —— 这是很多开发者踩坑的第一步。真正可用的递归语法是 connect by,而 with recursive 直到 oracle 12cr1 才正式支持(且需兼容模式),11gr2 完全不识别该关键字。所以所谓“在 11gr2 存储过程中用 with 优化递归”,本身就是一个伪命题。
为什么不能在 PL/SQL 里直接写 WITH 递归?
Oracle 11gR2 的 PL/SQL 解析器不接受 WITH 作为游标定义或动态 SQL 的顶层结构。常见报错包括:
PLS-00103: Encountered the symbol "WITH"ORA-32034: unsupported use of WITH clause
根本原因:11gR2 要求 WITH 必须紧邻 SELECT/INSERT/UPDATE/DELETE 出现,且不能嵌套在 PL/SQL 块声明区、游标定义或 EXECUTE IMMEDIATE 字符串开头(除非整个字符串是完整 DML 语句)。强行拼接会导致绑定变量丢失、执行计划无法复用、循环检测失效。
11gR2 中真正可行的递归方案:用 CONNECT BY + 索引优化
在 11gR2 环境下,CONNECT BY 是唯一稳定、可控、带内置循环防护的递归机制。性能瓶颈通常不在语法本身,而在数据访问路径:
- 确保
parent_id列上有索引(最好是(parent_id, id)复合索引,兼顾 START WITH 和 CONNECT BY 条件) - 避免在
CONNECT BY条件中使用函数(如UPPER(parent_id)),否则索引失效 - 用
LEVEL 显式限制深度,防止意外无限递归(<code>CONNECT BY自带nocycle,但不设上限仍可能耗尽 PGA) - 若需排序输出,优先用
ORDER SIBLINGS BY,比外层ORDER BY更高效
示例:
SELECT id, name, parent_id, LEVEL FROM org START WITH parent_id IS NULL CONNECT BY PRIOR id = parent_id ORDER SIBLINGS BY name;
存储过程里调用递归逻辑的正确姿势
不要试图把递归 SQL 塞进 PL/SQL 循环里逐行处理。11gR2 下最简健壮的做法是:
- 把递归查询封装成视图(非物化),例如:
CREATE VIEW org_tree_v AS SELECT ... CONNECT BY ... - 在存储过程中用
OPEN cur FOR SELECT * FROM org_tree_v WHERE root_id = p_root_id(注意:视图不能参数化,需靠 WHERE 过滤) - 或改用函数返回
SYS_REFCURSOR,但函数体内部仍只能用CONNECT BY,不能用WITH - 避免在过程里用
EXECUTE IMMEDIATE拼接递归 SQL —— 绑定变量难管理,硬解析多,且无法利用共享池中的递归执行计划
容易被忽略的深度陷阱:LEVEL 和 PRIOR 的方向一致性
性能差往往不是因为没索引,而是 CONNECT BY 走了全表扫描 —— 常见于 PRIOR 写反。比如树形结构查子节点,应写 CONNECT BY PRIOR id = parent_id;若误写为 PRIOR parent_id = id,Oracle 就无法利用 parent_id 索引,强制回表。另外,LEVEL 是伪列,不能用于索引,但可用于 WHERE LEVEL 提前剪枝,这个条件必须写在 <code>CONNECT BY 后、ORDER 前才生效。











