您的位置:

Oracle递归查询

一、递归查询初步

递归查询是指在一个结果集中,通过一些逻辑关系,不断地反复查询,直至满足最终条件的查询方式。

举例来说,如果有一个部门表(dept)和一个员工表(emp),每个员工都属于某个部门,而部门也可以有上级部门(自己可以做为上级部门也可以没有上级部门)。假设需要查询一个员工所属的最高部门,就需要进行递归查询。具体实现方式可以采用联接查询或者子查询,但最终都需要用到Oracle提供的递归查询方法。


--使用递归查询,在dept表中,查询deptno=1的部门的所有子部门
WITH dept_tree(deptno, parent_deptno, dept_name,dept_level) AS (
  SELECT deptno, parent_deptno, dept_name,0 AS dept_level
  FROM dept d
  WHERE d.deptno = 1
  UNION ALL
  SELECT d.deptno, d.parent_deptno, d.dept_name,t.dept_level+1
  FROM dept d, dept_tree t
  WHERE d.parent_deptno = t.deptno
)
SELECT deptno, dept_name,dept_level FROM dept_tree;

上面的代码中,使用with语句定义递归查询的临时表dept_tree,首先查询deptno=1的部门(根部门),在后续的查询中,通过和dept表的联接,再次查询在parent_deptno字段中出现depnto=1的子部门,直至子部门不再存在。最后查询出的结果集,就是deptno=1部门下的所有子部门。

二、递归查询的核心技术

递归查询的核心技术包括递归查询的定义和递归查询的终止条件。

1.递归查询的定义

递归查询的定义指的是要查询的逻辑关系,通常情况下是两张表之间的关系,比如上例中的员工表emp和部门表dept之间的关系。也有可能是同一张表中两个属性之间的层级关系,比如部门表中的parent_deptno和deptno属性的关系。


--使用递归查询,在emp表中,查询ename='SMITH'员工所属的最高部门
WITH dept_tree(deptno, parent_deptno, dept_level) AS (
  SELECT deptno, parent_deptno, 0 AS dept_level
  FROM dept d, emp e
  WHERE d.deptno=e.deptno AND e.ename='SMITH'
  UNION ALL
  SELECT d.deptno, d.parent_deptno,t.dept_level+1
  FROM dept d, dept_tree t
  WHERE d.deptno = t.parent_deptno
)
SELECT deptno, dept_level FROM dept_tree
WHERE parent_deptno IS NULL;

上面的代码中,使用了联接查询,使用dept表和emp表中deptno属性的关系,来查询ename='SMITH'员工在dept表中所属的部门。而在后续的查询,使用了部门表中的parent_deptno属性和deptno属性的关系,来实现递归查询。

2.递归查询的终止条件

递归查询的终止条件是指在递归查询时,满足某些条件就不再进行查询,直接返回结果。具体的终止条件的定义,取决于查询的需要和查询的逻辑关系。


--使用递归查询,在emp表中,查询empno=7369的员工的所有上级部门,直到根部门
WITH dept_tree(deptno, parent_deptno, dept_level) AS (
  SELECT deptno, parent_deptno, 0 AS dept_level
  FROM dept d, emp e
  WHERE d.deptno=e.deptno AND e.empno=7369
  UNION ALL
  SELECT d.deptno, d.parent_deptno,t.dept_level+1
  FROM dept d, dept_tree t
  WHERE d.deptno = t.parent_deptno AND t.parent_deptno IS NOT NULL
)
SELECT deptno, dept_level FROM dept_tree;

上面的代码中,定义了递归查询的终止条件:当查询到的部门的parent_deptno为null时,就停止递归查询,并返回结果集。这样,就可以查询7369员工所在的所有部门。

三、递归查询的优化

虽然使用递归查询可以方便地查询出满足条件的结果集,但是在性能方面,递归查询往往较慢。因此,在实际使用中,需要对查询进行优化,以提高性能。

1.递归查询的剪枝优化

递归查询的剪枝优化是指在递归查询过程中,根据某些条件,剪去一些不必要的查询路径,以减少递归查询次数,提高查询效率。


--使用递归查询,在emp表中,查询empno=7369的员工的所有上级部门,直到根部门,并进行剪枝优化
WITH dept_tree(deptno, parent_deptno, dept_level) AS (
  SELECT deptno, parent_deptno, 0 AS dept_level
  FROM dept d, emp e
  WHERE d.deptno=e.deptno AND e.empno=7369
  UNION ALL
  SELECT d.deptno, d.parent_deptno,t.dept_level+1
  FROM dept d, dept_tree t
  WHERE d.deptno = t.parent_deptno AND (t.parent_deptno IS NOT NULL OR d.deptno = 1)
)
SELECT deptno, dept_level FROM dept_tree;

上面的代码中,在dept表中,如果一个部门没有上级部门,那么它的parent_deptno字段的值为null。而在做递归查询时,如果连续查询到了null值,就会一直递归,直至查询结束。所以可以通过剪掉parent_deptno字段为null的查询路径,来提高查询效率。

2.递归查询的缓存优化

递归查询的缓存优化是指在递归查询过程中,对查询路径上的结果进行缓存,以避免重复查询和提高查询效率。


--使用递归查询,在emp表中,查询ename=SMITH员工所在的部门和所有上级部门,直到根部门,并进行缓存优化
WITH RECURSIVE dept_tree(deptno, dept_name, parent_deptno, dept_level) AS (
  SELECT deptno,dept_name,parent_deptno, 0 AS dept_level
  FROM dept d, emp e
  WHERE d.deptno=e.deptno AND e.ename='SMITH'
  UNION ALL
  SELECT d.deptno,d.dept_name,d.parent_deptno,t.dept_level+1
  FROM dept d, dept_tree t
  WHERE d.deptno = t.parent_deptno AND (t.parent_deptno IS NOT NULL OR d.deptno = 1)
)
SELECT deptno, dept_name, dept_level FROM dept_tree;

上面的代码中,采用了Oracle的WITH RECURSIVE关键字,对于递归查询结果集进行了缓存。这样,在递归查询中,每次查询到一条记录后,可以将这条记录加入查询结果缓存,下次查询时,如果查询条件和缓存中的条件相同,则直接从缓存中读取结果,避免重复查询,提高查询效率。

四、总结

Oracle递归查询是一种非常常用的查询方式,可以方便地查询出满足条件的结果集。但在实际使用中,为了提高查询效率,需要进行一些优化,比如剪枝优化和缓存优化。