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

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

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

1208328177
  • 自动化测试:
  • 测试文档:
一、语法51Testing软件测试网~v x RHL1zw*Z
大致写法:select * from some_table [where 条件1] connect by [条件2]startwith[条件3];
C\.L9n[H)@)X3qAa0其中 connect by 与startwith语句摆放的先后顺序不影响查询的结果,[where 条件1]可以不需要。
3|d%pyW k{.h)jp0[where 条件1]、[条件2]、[条件3]各自作用的范围都不相同:51Testing软件测试网a*IeJ-Iw/y

2UWo4f n/w0[where 条件1]是在根据“connect by [条件2]startwith[条件3]”选择出来的记录中进行过滤,是针对单条记录的过滤, 不会考虑树的结构;
M` f+@P W0Uu0
*l.I3D0y~~W#w4]N0[条件2]指定构造树的条件,以及对树分支的过滤条件,在这里执行的过滤会把符合条件的记录及其下的所有子节点都过滤掉;
sgc]1CC051Testing软件测试网X[+r@7O(~
[条件3]限定作为搜索起始点的条件,如果是自上而下的搜索则是限定作为根节点的条件,如果是自下而上的搜索则是限定作为叶子节点的条件;51Testing软件测试网H]\?,J
51Testing软件测试网w&Vvx vWU
示例:
2X-|.A;U.aZ.tj|0假如有如下结构的表:some_table(id,p_id,name),其中p_id保存父记录的id。
-[0FI3oL hc7Jm0select * 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软件测试网sKY:D!Z*qb5D

5vod!j!A!vc0对prior的说明:51Testing软件测试网;?"z ExJ9`0^"l/Q
    prior存在于[条件2]中,可以不要,不要的时候只能查找到符合“startwith[条件3]”的记录,不会在寻找这些记录的子节点。要的时候有两种写法:connect by prior t.p_id=t.id 或 connect by t.p_id=prior t.id,前一种写法表示采用自上而下的搜索方式(先找父节点然后找子节点),后一种写法表示采用自下而上的搜索方式(先找叶子节点然后找父节点)。51Testing软件测试网&YpR-wIn{}
51Testing软件测试网mSy7L(M-a8g`
二、执行原理
&e z8[\ k)Y0connect by...startwith...的执行原理可以用以下一段程序的执行以及对存储过程RECURSE()的调用来说明:
5zZ|]j ujd|0
(?qjM^5`'a ?[%z0/* 遍历表中的每条记录,对比是否满足startwith后的条件,如果不满足则继续下一条,51Testing软件测试网$UL/a7|+^K
如果满足则以该记录为根节点,然后调用RECURSE()递归寻找该节点下的子节点,51Testing软件测试网/Lq&C \ v1aL
如此循环直到遍历完整个表的所有记录 。*/
-D)G)Hx6i{0a0for rec in (select * from some_table) loop51Testing软件测试网$d-D `*kZ/X%f(Yz
if FULLFILLS_START_WITH_CONDITION(rec) then51Testing软件测试网?_C!T [$Bm7V
    RECURSE(rec, rec.child);
Gnq#y OH.~B:K}0end if;
Om\&{E"C ssQ0end loop;51Testing软件测试网\*G2\^[0? w X E

o6Nx3{DH/lu0/* 寻找子节点的存储过程*/
/wT7_0~g0procedure RECURSE (rec in MATCHES_SELECT_STMT, new_parent IN field_type) is51Testing软件测试网n'b\7{I1M]O,O/B
begin51Testing软件测试网,]Y:g)R"|,Q
APPEND_RESULT_LIST(rec); /*把记录加入结果集合中*/
?@%wl;c"Zt0/*再次遍历表中的所有记录,对比是否满足connect by后的条件,如果不满足则继续下一条,
,R!v_*?@ma%U.P0如果满足则再以该记录为根节点,然后调用RECURSE()继续递归寻找该节点下的子节点,
#t3Rw#^3}Y0如此循环直到找至叶子节点。*/51Testing软件测试网0gD V P1_*d {{3h
for rec_recurse in (select * from some_table) loop51Testing软件测试网E4JW&^V(??-C(~5Lu
    if FULLFILLS_CONNECT_BY_CONDITION(rec_recurse.child, new_parent) then
}-[,i?k'QS[7`0      RECURSE(rec_recurse,rec_recurse.child);
w z;K8V.C@2j0    end if;51Testing软件测试网 D!a,Ms6Z x Z#n`*b
end loop;51Testing软件测试网/t$pI&w@~
end procedure RECURSE;
M2}*jdJa9A mu051Testing软件测试网K wV;t?
三、使用探讨51Testing软件测试网c _D*W4| G:}'|
    从上面的执行原理可以看到“connect by...startwith...”构造树的方式是:(1)如果是自上而下方式,则把表中的每一条记录都作为根节点来生成树,所以表中有多少条记录就会构造出多少棵树。(2)如果是自下而上的搜索方式,则把表中的每一条记录都作为叶子节点来生成分支,所以表中有多少条记录就会生成多少条分支。
EP2T)]0n i$x0    因此如果表中的记录不是严格遵照每条记录都只能有一个父记录的原则,那么就可能有部分记录会存在于多棵树中,那么在查找记录的时候就可能会出现找到多条重复记录的异常情况。[align=left][/align]
Start with...Connect By子句递归查询一般用于一个表维护树形结构的应用。
创建示例表:
CREATE TABLE TBL_TEST51Testing软件测试网#IT `?o|;D
(51Testing软件测试网l0?"@?$w L8n9C0q}
  ID    NUMBER,
o@ ](`A|{ c0  NAME  VARCHAR2(100 BYTE),
2q\ w'M2|8N0  PID   NUMBER                                  DEFAULT 051Testing软件测试网 `S,LF#V\
);
 
插入测试数据:
INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('1','10','0');
)I8DB/c^Ma6je[0INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('2','11','1');51Testing软件测试网7ppQ,kf/{
INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('3','20','0');
Pn/u"SUO1r0INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('4','12','1');
5cw+Y;m7vLjzL0INSERT INTO TBL_TEST(ID,NAME,PID) VALUES('5','121','2');
 
从Root往树末梢递归
select * from TBL_TEST
H@`w6y0 start with id=1
:{ @.N|7E q"bY0 connect by prior id = pid
 
从末梢往树ROOT递归
select * from TBL_TEST51Testing软件测试网v#Ox4T:l O*j7[OD
 start with id=5
:a`(g)R8N(R$^9w0}0 connect by prior pid = id
=====

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

51Testing软件测试网e:tM:lc~

 51Testing软件测试网u:d _ bAn6l-C9m:E

DEPTIDPAREDEPTIDNAME
NUMBERNUMBERCHAR (40 Byte)
部门id父部门id(所属部门id)部门名称

Xr O3c8p.S+BA0 51Testing软件测试网"[mjZsa

51Testing软件测试网 K1{9m6Y(_ GF%J9K{

通过子节点向根节点追朔.51Testing软件测试网.T)}w,YNO3u

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   

!_yQ;RTjcp3S]{0 

Vej)| QM0

"E]bEZ'C$f5d+bK0通过根节点遍历子节点.51Testing软件测试网;z7ySOK)A"X^

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   

u|G)\['Lc5e0 

v;KK4FN051Testing软件测试网0r9V6U h1R+[}/x%R

可通过level 关键字查询所在层次.51Testing软件测试网#pC*I Pg!^2a;{q

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   
51Testing软件测试网0XN%k"jV3U`

 

\*Y1t%a/F y051Testing软件测试网MU|#~q)o1J+cF&X

再次复习一下:start with ...connect by 的用法,start with后面所跟的就是就是递归的种子51Testing软件测试网"_.f+O"OL#Y9E

]o9^rHhCOI0递归的种子也就是递归开始的地方connect by 后面的"prior" 如果缺省:则只能查询到符合条件的起始行,并不进行递归查询;51Testing软件测试网.w wx6^/E6|G

51Testing软件测试网3\P3[;^0uje

connect by prior 后面所放的字段是有关系的,它指明了查询的方向51Testing软件测试网2iz1E0N*pSN

51Testing软件测试网!wlY$e@5_+[w

练习: 通过子节点获得顶节点51Testing软件测试网B9U"p4E&qaP

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  
51Testing软件测试网&@ R"dD0b+F_l|"?

====这种方法只是当表里就有一颗树,多棵树怎么办?51Testing软件测试网'i+s1],X:Xc_]7|

s R ?1l(z A'}0
51Testing软件测试网&h8|`2ZMLo

 

GRx}9U)`$t0

TAG:

 

评分:0

我来说两句

Open Toolbar