oracle里怎么用那个递归的sql写法举个简单例子说说
- 问答
- 2026-01-26 04:25:21
- 6
在Oracle数据库里,递归SQL写法是一种让查询能够处理像树形或层次结构数据的方法,比如公司里员工和经理的上下级关系,或者分类目录中的父子类别,这种写法可以让查询自己调用自己,一层一层地挖掘数据,直到找完所有相关记录,在Oracle中,主要有两种方式来做递归查询:一种是老式的CONNECT BY语法,另一种是新式的使用WITH子句的递归写法,这里,我会用简单例子详细说明这两种方法,让你明白怎么用。
说说老式的CONNECT BY写法,这种方式在Oracle里用了很多年,看起来直白,假设我们有一个员工表,叫employees,里面有两个字段:emp_id(员工编号)和manager_id(经理编号),经理编号指向这个员工的上级是谁;如果员工是最高层,比如老板,经理编号就是空值,我们想列出所有员工,并显示他们的层级关系,用CONNECT BY来写,查询语句是这样的:SELECT emp_id, manager_id, LEVEL FROM employees START WITH manager_id IS NULL CONNECT BY PRIOR emp_id = manager_id,这里,START WITH子句指定了从哪里开始递归,这里是从经理编号为空的员工开始,也就是顶层,CONNECT BY子句定义了父子关系:PRIOR emp_id = manager_id表示上一行的员工编号等于当前行的经理编号,LEVEL是一个Oracle自动提供的列,表示当前行在树中的层级,顶层是1,下一层是2,依此类推,这个查询会从顶层员工开始,然后找他们的下属,再找下属的下属,一直递归下去,直到没有更多下属为止,举个例子,如果员工表里有数据:emp_id 1是老板(manager_id为空),emp_id 2的经理是1,emp_id 3的经理是2,那么查询结果会显示1在层级1,2在层级2,3在层级3,这种方式简单,但功能有限,比如不容易在递归过程中做复杂计算。
重点讲讲新式的WITH子句递归写法,这种方法更灵活,也符合SQL标准,在Oracle 11g及以后版本都支持,它使用WITH关键字定义一个临时结果集,然后在这个结果集里自己引用自己,实现递归,基本结构是:先写WITH子句给临时结果集起个名字,比如叫recursive_data,然后定义列列表,接着用AS跟一个括号,括号里写两个部分用UNION ALL连接:第一部分是初始查询,也就是递归的起点;第二部分是递归查询,这里会引用临时结果集自己,用一个主查询从这个临时结果集选数据,听起来有点绕,我举个具体例子。
还是用那个员工表employees,有emp_id和manager_id字段,我们想查询所有员工,并显示每个员工的层级,以及从顶层到该员工的路径,用WITH递归写法,查询可以这样写:WITH emp_hierarchy (emp_id, manager_id, level, path) AS (SELECT emp_id, manager_id, 1, CAST(emp_id AS VARCHAR2(100)) FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.emp_id, e.manager_id, eh.level + 1, eh.path || '->' || e.emp_id FROM employees e JOIN emp_hierarchy eh ON e.manager_id = eh.emp_id) SELECT FROM emp_hierarchy,我来一步步解释,WITH emp_hierarchy定义了一个临时结果集,叫emp_hierarchy,它包含四列:emp_id、manager_id、level和path,AS后面的括号里是递归定义,初始查询是SELECT emp_id, manager_id, 1, CAST(emp_id AS VARCHAR2(100)) FROM employees WHERE manager_id IS NULL,这选取了所有没有经理的员工(即顶层),设置level为1,path为员工编号(用CAST转换成字符串,方便后面拼接),然后UNION ALL连接递归查询:SELECT e.emp_id, e.manager_id, eh.level + 1, eh.path || '->' || e.emp_id FROM employees e JOIN emp_hierarchy eh ON e.manager_id = eh.emp_id,这里,employees表(别名叫e)和emp_hierarchy临时结果集(别名叫eh)做连接,连接条件是e.manager_id = eh.emp_id,意思是找那些经理是当前层级员工的员工,也就是下属,每次递归,level加1,path则在之前路径上加上新员工编号,主查询SELECT FROM emp_hierarchy会输出所有结果。

这个查询是怎么运行的呢?初始查询执行一次,得到所有顶层员工,比如emp_id 1,然后递归查询开始:用初始结果集(层级1)连接employees表,找到经理为1的员工,比如emp_id 2,level变成2,path变成"1->2",这些结果加到emp_hierarchy里,递归查询再次运行,但这次emp_hierarchy包含了层级2的员工,所以能找到经理为2的员工,比如emp_id 3,level变成3,path变成"1->2->3",这个过程一直重复,直到递归查询不再返回新行,也就是没有更多下属为止,主查询从emp_hierarchy选出所有行,显示完整层级。
为了更清楚,我举个数字例子,假设employees表有三行数据:emp_id 1的manager_id为空;emp_id 2的manager_id是1;emp_id 3的manager_id是2,运行查询后,初始查询得到(1, null, 1, '1'),第一次递归:用(1, null, 1, '1')连接表,找到manager_id=1的员工,即emp_id 2,结果(2, 1, 2, '1->2'),第二次递归:现在emp_hierarchy有(1, null, 1, '1')和(2, 1, 2, '1->2'),用这些连接表,找到manager_id=2的员工,即emp_id 3,结果(3, 2, 3, '1->2->3'),第三次递归:用更新后的emp_hierarchy连接表,没有员工manager_id是3,所以返回空,递归停止,最终输出三行:层级1、2、3各一行。
这种WITH递归写法比CONNECT BY更强大,因为可以在递归过程中做更多事,如果我们只想查看到第三层员工,可以在递归查询里加条件:WHERE eh.level < 3,这样递归到第三层就停止,或者,如果想计算累计工资,可以在递归部分添加字段,如果数据有循环引用(比如员工A的经理是B,B的经理又是A),递归可能无限循环,Oracle默认会检测到并报错,但可以用CYCLE子句处理,不过为了避免专业术语,这里不深入;简单说,写查询时要注意数据是否合理。

对比一下两种写法,CONNECT BY语法简单,适合快速查询层次结构,但功能有限;WITH递归写法更灵活,能处理复杂逻辑,而且代码更容易读和维护,根据Oracle官方文档,递归CTE(即WITH写法)是SQL:1999标准的一部分,推荐在新项目中使用,如果数据库版本较旧,可能只支持CONNECT BY。
再举一个例子,比如分类表categories,有category_id(分类编号)和parent_id(父分类编号)字段,想找出某个分类的所有子分类,用WITH递归写法:WITH category_tree (category_id, parent_id, level) AS (SELECT category_id, parent_id, 1 FROM categories WHERE parent_id IS NULL UNION ALL SELECT c.category_id, c.parent_id, ct.level + 1 FROM categories c JOIN category_tree ct ON c.parent_id = ct.category_id) SELECT * FROM category_tree,这和员工表示例类似,只是换了字段名,初始查询找顶层分类(父分类为空),递归查询逐层找子分类。
在实际使用中,递归SQL的性能要注意,如果数据量很大,递归查询可能会慢,因为要反复连接表,为了加速,可以在连接字段上建索引,比如在manager_id或parent_id上建索引,递归深度默认有限制,但通常够用;如果层级特别深,可能需要调整数据库设置。
在Oracle里用递归SQL,你可以选CONNECT BY或WITH递归写法,CONNECT BY直接,用START WITH和CONNECT BY PRIOR就行;WITH递归写法更现代,用WITH子句定义初始和递归部分,通过员工表和分类表的例子,你可以看到怎么写递归查询来遍历层次数据,关键点是:递归查询自己调用自己,直到没数据为止;写的时候要确保有终止条件,避免无限循环,希望这些例子能帮你理解,如果你有具体数据,可以试试这些查询,调整字段名和条件,递归SQL是处理树形数据的好工具,在Oracle里用起来不难。
本文由凤伟才于2026-01-26发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://dems.haoid.cn/wenda/86036.html
