暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

SQL之美第八篇:WITH 语句深度研究2

晟数学苑 2021-08-10
384

接上一篇:

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”。我们可以将选择列称为我们喜欢的任何合适的列名,这是自动的添加到我们的结果中,以便我们的最终查询可以按照我们想要的顺序对数据进行排序。

  除了分层数据处理之外,递归子查询因子子句可以有许多用途; 远远超出日常业务代码的想象,那些固定逻辑思维,比如分割数据 ,下一篇继续

文章转载自晟数学苑,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论