不谋万世者,不足谋一时;不谋全局者,不足谋一域。君子敏于事而慎于言 新浪微薄:土司阿哈

树-Oracle用Start with...Connect By子句递归查询

上一篇 / 下一篇  2010-01-20 19:24:27 / 个人分类:软件开发相关

1208328177
  • 自动化测试:
  • 测试文档:
一、语法51Testing软件测试网4sPvy.?NU#Q
大致写法:select * from some_table [where 条件1] connect by [条件2]startwith[条件3];51Testing软件测试网pu1\ @!vf h
其中 connect by 与startwith语句摆放的先后顺序不影响查询的结果,[where 条件1]可以不需要。
JvT A'y)uzV(x0[where 条件1]、[条件2]、[条件3]各自作用的范围都不相同:51Testing软件测试网3AI0qH3qA)S

3W[:a l{R|Os2S&h0[where 条件1]是在根据“connect by [条件2]startwith[条件3]”选择出来的记录中进行过滤,是针对单条记录的过滤, 不会考虑树的结构;51Testing软件测试网5qcH \ Y'{A%l
51Testing软件测试网!J0T$eK/L$k
[条件2]指定构造树的条件,以及对树分支的过滤条件,在这里执行的过滤会把符合条件的记录及其下的所有子节点都过滤掉;
#x/{!ae};K051Testing软件测试网iFjV8{]YMb
[条件3]限定作为搜索起始点的条件,如果是自上而下的搜索则是限定作为根节点的条件,如果是自下而上的搜索则是限定作为叶子节点的条件;51Testing软件测试网\U:Lz Sd+N_u
51Testing软件测试网8j6^hpF7i@*YP
示例:
(ssdGxO l!r3a!Q0假如有如下结构的表:some_table(id,p_id,name),其中p_id保存父记录的id。51Testing软件测试网{4N5iZ%_d,mv)N;`
select * from some_table t where t.id!=123 connect by prior t.p_id=t.id and t.p_id!=321startwitht.p_id=33 or t.p_id=66;51Testing软件测试网uRd'{7C)n;l qx!ra
51Testing软件测试网!whC ~!\#D9t3X
对prior的说明:51Testing软件测试网5?REZ[~2k[(r J G'Xz
    prior存在于[条件2]中,可以不要,不要的时候只能查找到符合“startwith[条件3]”的记录,不会在寻找这些记录的子节点。要的时候有两种写法:connect by prior t.p_id=t.id 或 connect by t.p_id=prior t.id,前一种写法表示采用自上而下的搜索方式(先找父节点然后找子节点),后一种写法表示采用自下而上的搜索方式(先找叶子节点然后找父节点)。
dwv g(K%Aw051Testing软件测试网4] B8sW}`7N
二、执行原理
S/s U7MJ+^6~ wZ0connect by...startwith...的执行原理可以用以下一段程序的执行以及对存储过程RECURSE()的调用来说明:51Testing软件测试网5|0W8l}a px~!k
51Testing软件测试网0OUj+s"?z
/* 遍历表中的每条记录,对比是否满足startwith后的条件,如果不满足则继续下一条,51Testing软件测试网Ub-V0Sa[#J9O _[
如果满足则以该记录为根节点,然后调用RECURSE()递归寻找该节点下的子节点,51Testing软件测试网b6Nvn ? T3A
如此循环直到遍历完整个表的所有记录 。*/
,e I9T\!q0for rec in (select * from some_table) loop51Testing软件测试网MSrg E5p$hh
if FULLFILLS_START_WITH_CONDITION(rec) then
0f#K[D9C6MOf'q9V0    RECURSE(rec, rec.child);
1O+Pwxp?(E tz0end if;
hGg)S8?9T]B6DV7s!S0end loop;
]t q1qA:kTX0
^^7UQ6q0/* 寻找子节点的存储过程*/
2zP/` F"[1PK?0procedure RECURSE (rec in MATCHES_SELECT_STMT, new_parent IN field_type) is51Testing软件测试网+{Yf.F%U"C7\4~C
begin
nI-Z*b,CJ+B-|XG(a(kO0APPEND_RESULT_LIST(rec); /*把记录加入结果集合中*/51Testing软件测试网hms4ir(J%i])|
/*再次遍历表中的所有记录,对比是否满足connect by后的条件,如果不满足则继续下一条,51Testing软件测试网l2I/N{I$q'~!J1M
如果满足则再以该记录为根节点,然后调用RECURSE()继续递归寻找该节点下的子节点,
[FC*wi-l`0如此循环直到找至叶子节点。*/
Sy#u-qs5t0for rec_recurse in (select * from some_table) loop51Testing软件测试网#Lqu4Gz@PG
    if FULLFILLS_CONNECT_BY_CONDITION(rec_recurse.child, new_parent) then
7OHj^xD~#B"~0e0      RECURSE(rec_recurse,rec_recurse.child);
u;[3R-g w0    end if;
KjY!Zh0B4bp0end loop;51Testing软件测试网 p4\^ ul \0|;t
end procedure RECURSE;51Testing软件测试网+T T \QJ1RH

F U?7`P#W`@0三、使用探讨51Testing软件测试网9`H)l"T L1QO
    从上面的执行原理可以看到“connect by...startwith...”构造树的方式是:(1)如果是自上而下方式,则把表中的每一条记录都作为根节点来生成树,所以表中有多少条记录就会构造出多少棵树。(2)如果是自下而上的搜索方式,则把表中的每一条记录都作为叶子节点来生成分支,所以表中有多少条记录就会生成多少条分支。
YI{@&[`}v0    因此如果表中的记录不是严格遵照每条记录都只能有一个父记录的原则,那么就可能有部分记录会存在于多棵树中,那么在查找记录的时候就可能会出现找到多条重复记录的异常情况。[align=left][/align]
Start with...Connect By子句递归查询一般用于一个表维护树形结构的应用。
创建示例表:
CREATE TABLE TBL_TEST51Testing软件测试网Ql5v]4q%b6i'?\
(51Testing软件测试网1uE+JhHiT,M0kK
  ID    NUMBER,51Testing软件测试网6^fN.A D2F PA~
  NAME  VARCHAR2(100 BYTE),
,RWa H,ZE0  PID   NUMBER                                  DEFAULT 0
s2aL!z*g{]*c:I0);
 
插入测试数据:
INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('1','10','0');51Testing软件测试网O!E-_0P/M+SY,O*Ty \G*i
INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('2','11','1');
y!~)}mddP0INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('3','20','0');51Testing软件测试网Qj }R!\)|v$I
INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('4','12','1');51Testing软件测试网-|;q+F4V)qcHj:|S~
INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('5','121','2');
 
从Root往树末梢递归
select * from TBL_TEST51Testing软件测试网B mc2t [0E&w6kb~v:b
 start with id=1
S6]4f`Ic{N0 connect by prior id = pid
 
从末梢往树ROOT递归
select * from TBL_TEST51Testing软件测试网B kd['Z&r7H
 start with id=5
J*T&G R T9Zf*zw O d0 connect by prior pid = id
=====

对于oracle进行简单树查询(递归查询)

51Testing软件测试网J7q|:T(Z6t^q;gS

 

5j6OX#y^0
DEPTIDPAREDEPTIDNAME
NUMBERNUMBERCHAR (40 Byte)
部门id父部门id(所属部门id)部门名称
51Testing软件测试网deo)y!T\VgJ

 51Testing软件测试网 p!_W6dq D%]'o1_(Xp

51Testing软件测试网} \ z;K*}n}4M

通过子节点向根节点追朔.51Testing软件测试网!hn.^(va?

Sql代码复制代码
  1. select * from persons.dept start with deptid=76 connect by prior paredeptid=deptid   
Sql代码复制代码
  1. select * from persons.dept start with deptid=76 connect by prior paredeptid=deptid   

/zC Rvzg+K7O0 51Testing软件测试网,m3k5`7O._Q

N-J&]/h \O vI0通过根节点遍历子节点.51Testing软件测试网+F,ZNA*]'tk

Sql代码复制代码
  1. select * from persons.dept start with paredeptid=0 connect by prior deptid=paredeptid   
Sql代码复制代码
  1. select * from persons.dept start with paredeptid=0 connect by prior deptid=paredeptid   

{3O3wdf3E!tb(p0 51Testing软件测试网*oB-O3x,o;Ff

r^/~/a)l0可通过level 关键字查询所在层次.51Testing软件测试网Z&}+Z _;@N_+}

Sql代码
  1. select a.*,level from persons.dept a start with paredeptid=0 connect by prior deptid=paredeptid   
Sql代码复制代码
  1. select a.*,level from persons.dept a start with paredeptid=0 connect by prior deptid=paredeptid   

[O4h9?a gI0 51Testing软件测试网O1He5A`YyV'j)H p/~

'QX0NeD-ZyW(r0再次复习一下:start with ...connect by 的用法,start with后面所跟的就是就是递归的种子51Testing软件测试网7V E(n\&NA(q

)I/b1V"Uy T0递归的种子也就是递归开始的地方connect by 后面的"prior" 如果缺省:则只能查询到符合条件的起始行,并不进行递归查询;

3X.nZ#Bp!?8_051Testing软件测试网L~4s&u8^H`)s'n U

connect by prior 后面所放的字段是有关系的,它指明了查询的方向

_;Z7w2hhy2SOh0

C!j"OU*XG0练习: 通过子节点获得顶节点

$I+Pi$p'Xo$tN0
Sql代码复制代码
  1. select FIRST_VALUE(deptid) OVER (ORDER BY LEVEL DESC ROWS UNBOUNDED PRECEDING) AS firstdeptid from persons.dept start with deptid=76 connect by prior paredeptid=deptid  

ysTNE4\m0K0====这种方法只是当表里就有一颗树,多棵树怎么办?
l6l)p9P,e0
51Testing软件测试网(v7],m j;z&c'X

b e AQ1C0 

&\IqVQ*}0

TAG:

 

评分:0

我来说两句

Open Toolbar