接上一篇:
Subquery Factoring
从Oracle 11gR2开始,子查询因子分解(with)子句得到了增强,包括递归处理。那意味着因子子查询可以引用自身。如果我们尝试在10g中执行此操作,则会出现异常报错ORA-32031:在WITH子句中非法引用查询名称。
虽然不是非常具有描述性,但至少比说它更具说明性的语法无效好多了,且基本上告诉你,如果引用其他先前的WITH子句查询,就可以了,但不是与查询本身相同递归的基础究竟是什么,让我们从一个简单的PL/SQL示例代码开始:
create or replace procedure recursion_test(start_r in number
,end_r in number) as
begin
if start_r < end_r then
recursion_test(start_r+1, end_r);
end if;
dbms_output.put_line(start_r);
end;
/
---------SQLPlus/命令行中运行PL/SQL块前,如果要将执行结果输出,需要先执行 set serveroutput on 命令,
---------在窗口里显示服务器输出信息。再用dbms_output.put_line()语句输出变量值
SQL> set serveroutput on;
SQL> exec recursion_test(1,5);
5
4
3
2
1
PL/SQL procedure successfully completed
令人疑惑的地方在于为什么结果集是倒叙排序的,从5开始一直到1结束,PL/SQL实际的内部工作顺序究竟是怎样的,接下来我们反复调用,
提供调用逻辑顺序:
start_r=1,end_r=5
start_r=2,end_r=5 ,
start_r=3,end_r=5,
start_r=4 end_r=5,
start_r=5 end_r=5
执行返回调用代码内容为:start_r=4, end_r=5 时候输出start_r的值为4,
执行返回调用代码内容为:start_r=3,end_r=5 时候输出start_r的值为3
执行返回调用代码内容为:start_r=2,end_r=5 时候输出start_r的值为2
执行返回调用代码内容为:start_r=1,end_r=5 时候输出start_r的值为1

每次存储过程调用时候,它都会在内存中创建一个单独的过程实例,任何存储过程,一旦执行完该过程实例,执行将会返回到调用代码,没有条件组织代码调用自己,会导致oracle最终耗尽内存资源,或者代码fatial,所以在应用程序中,必须有一些限制条件是可以停止代码自身。


这是一个深度的递归层次搜索,在此示例中,代码使用我们的“mgr经理并显示其详细信息,然后获取所有直接员工的列表属于哪个经理,并且对于每个人都称该员工为经理。level”(lvl)表示执行堆栈中过程调用的深度,0表示第一次调用,1是0的调用,
依此类推
下面我们通过sql语句分层来做个比较 :

我们得到相同的结果。CONNECT BY查询还具有level伪值,该值同样表示深度级别,在它自己的递归性质内遍历层次结构。
那么,递归的关键特征是什么?
1.递归过程以一些初始值或值开始。(从...开始)
2.递归过程引用自身,通常使用基于当前值的新值(CONNECT BY)
3.递归过程有一些“退出”条件,以防止进一步递归(由CONNECT BY暗示)
那么 这与WITH子句和递归子查询因子分解有什么关系呢?
重新开始:
基于我们的递归功能,让我们开始构建一个WITH子句,它可以重现我们上面做的EMP层次结构。我们将从START WITH数据开始
with emp_hier(mgr, lvl) as (
select empno, 1 from emp where mgr is null
)
select '['||to_char(lvl)||']'||lpad(' ',lvl,' ')||to_char(mgr) as result
from emp_hier
/
ESULT
--------
[1] 7839
对于Subquery Factoring,列的别名应直接在括号中指定查询名称,虽然上面的查询不需要,但在我们进行时将需要它,否则会出现例外
在递归子查询因子术语中,上述“起始”数据被称为Anchor Member。现在我们想要使用这个WITH子查询引用本身,那么我们该怎么做呢?
很简单,我们在同一个WITH子句中包含第二个查询(称为递归成员),并加入这两个查询一起使用UNION ALL语句
with emp_hier(mgr, lvl) as (
-- ANCHOR QUERY
select empno, 1 from emp where mgr is null
union all
-- RECURSIVE QUERY
select emp.empno
,emp_hier.lvl+1
from emp
join emp_hier on (emp.mgr = emp_hier.mgr)
)
select '['||to_char(lvl)||']'||lpad(' ',lvl,' ')||to_char(mgr) as result
from emp_hier
/
RESULT
[1] 7839
[2] 7566
[2] 7698
[2] 7782
[3] 7499
[3] 7521
[3] 7654
[3] 7788
[3] 7844
[3] 7900
[3] 7902
[3] 7934
[4] 7369
[4] 7876
此递归成员查询正在执行的操作是查询EMP表中的数据,其中管理器由提供者提供emp_hier子查询中的数据。Oracle非常聪明地理解,在第一个实例中,数据来自Anchor Member查询,然后后续迭代来自与其联合的数据,因此它将继续操作,只要有进一步的EMP记录就会一直递归下去

在这种情况下,我们告诉它我们希望深度优先搜索,按照mgr值的顺序,在新的中设置整体排序值列名为“rn”。我们可以将选择列称为我们喜欢的任何合适的列名,这是自动的添加到我们的结果中,以便我们的最终查询可以按照我们想要的顺序对数据进行排序。
除了分层数据处理之外,递归子查询因子子句可以有许多用途; 远远超出日常业务代码的想象,那些固定逻辑思维,比如分割数据 ,下一篇继续





