start with ... connect by 递归查询

Posted codebetter

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了start with ... connect by 递归查询相关的知识,希望对你有一定的参考价值。

语法

start with ... connect by语法:

select ... from tablename 
start with 条件1 
connect by prior 条件2 
where 条件3

例如:

select * from table
start with org_id = '***'
connect by prior org_id = parent_id;

简介

简单说来是将一个树状结构存储在一张表里,比如一个表中存在两个字段:
org_id,parent_id那么通过表示每一条记录的parent是谁,就可以形成一个树状结构。

用上述语法的查询可以取得这棵树的所有记录。
其中:
? 条件1 是根结点的限定语句,当然可以放宽限定条件,以取得多个根结点,实际就是多棵树。
? 条件2 是连接条件,其中用PRIOR表示上一条记录,比如 CONNECT BY PRIOR org_id = parent_id;就是说上一条记录的org_id 是本条记录的parent_id,即本记录的父亲是上一条记录。
? 条件3 是过滤条件,用于对返回的所有记录进行过滤。

简单介绍如下:
? 在扫描树结构表时,需要依此访问树结构的每个节点,一个节点只能访问一次,其访问的步骤如下:
? 第一步:从根节点开始;
? 第二步:访问该节点;
? 第三步:判断该节点有无未被访问的子节点,若有,则转向它最左侧的未被访问的子节,并执行第二步,否则执行第四步;
? 第四步:若该节点为根节点,则访问完毕,否则执行第五步;
? 第五步:返回到该节点的父节点,并执行第三步骤。
? 总之:扫描整个树结构的过程也即是中序遍历树的过程。

查询实例

创建如下表,并插入数据:

create table DEMO (
ID varchar2(10) primary key,
DSC varchar2(100),
PID varchar2(10)
)
--插入几条数据
Insert Into DEMO values ('00001', '中国', '-1');
Insert Into DEMO values ('00011', '陕西', '00001');
Insert Into DEMO values ('00012', '贵州', '00001');
Insert Into DEMO values ('00013', '河南', '00001');
Insert Into DEMO values ('00111', '西安', '00011');
Insert Into DEMO values ('00112', '咸阳', '00011');
Insert Into DEMO values ('00113', '延安', '00011');

树形图如图:

技术图片

自底向上的递归

Select * From DEMO
Start With ID = '00113' --以id=00113作为树的根开始递归
Connect By  Prior PID = ID --上一条的pid作为这一条的id(因为已知条件为id='00113',所以寻找本条记录的pid向上递归)

--结果
00113   延安  00011
00011   陕西  00001
00001   中国  -1

反映在如图所示的树上就为:从延安开始查询,查询到延安的父节点,然后以延安的父节点为根,查询延安的父节点(也就是陕西)的父节点,以此递归向上查询。

自上向下的递归

--自上向下
Select * From DEMO
Start With ID = '00001' -- 以id='00001'作为根递归查询
Connect By  Prior ID = PID -- 上一条记录的id作为本条记录的pid来查询,因为已知条件为id='00001',故以id='00001'为根,向下递归查询

--结果
00001   中国  -1
00011   陕西  00001
00111   西安  00011
00112   咸阳  00011
00113   延安  00011
00012   贵州  00001
00013   河南  00001

结合上图所示的树:从中国开始查询,访问根节点,再遍历左子树,即树的先序遍历。

加where子句的递归查询

Select ID, PID, DSC
From DEMO
WHERE ID <> '00011'  -- 不能在最后部分加where,不能在connect by最后面再加.
Start With ID = '00001'
Connect By Prior ID =  PID

--结果
00001   -1      中国
00111   00011   西安
00112   00011   咸阳
00113   00011   延安
00012   00001   贵州
00013   00001   河南

可以看成与递归查询无关.只是对整个结果起过滤作用。

nocycle关键字、level关键字、connect_by_isleaf isLeaf

在Oracle中的role是禁止出现循环的.比如你grant A to B ,grant B to C .再来个grant C to A会出错的。如果树中有环的话在递归查询中会报错,可以用nocycle关键字来消除环。

如:假设上述的表中 ID ‘00001‘ 记录的 ‘PID‘ 也改为 ‘00001‘, 会出现循环的问题, 这时,需要用到 nocycle 即可消除循环。

Select ID, PID, DSC,
connect_by_isleaf isLeaf, -- 表示当前节点是否是叶子节点
LEVEL -- 表示当前节点所处层级, 这里的层级指的是 从 start with 查询到的节点开始往下算起, 当前属于第几层级
From DEMO
Connect By nocycle Prior ID = PID -- 在这里加入nocycle
Start With ID = '00001';

-- 结果
Select ID, PID, DSC,
connect_by_isleaf isLeaf,
LEVEL
From DEMO
Connect By nocycle Prior ID = PID
Start With ID = '00001';

--结果
ID      PID     DSC  isLeaf     LEVEL
00001   00001   中国  0           0
00011   00001   陕西  0           1
00111   00011   西安  1           2
00112   00011   咸阳  1           2
00113   00011   延安  1           2
00012   00001   贵州  1           1
00013   00001   河南  1           1

这里需要注意的一个点, 如果采用的是 自底向上的 方式查询, 则 LEVEL 的 层级 同样是 从底向上, 如 00113 LEVEL 1 00011 LEVEL 2 00001 LEVEL 3。

另外一点: 如果在查询语句中 Select ID, PID, DSC, connect_by_isleaf isLeaf, LEVEL - 1 LEVEL 这种查询方式的话, 在 WHERE 判断条件中, 只需要判断 LEVEL = 1, 就可以取出 当前查询节点的子节点(由于LEVEL 也是 伪列, 需要用子查询的方式);

SIBLINGS关键字、connect_by_iscycle

它会保护层次,并且在每个等级中按expre排序。

Select ID, PID, DSC,
connect_by_isleaf, -- 存在循环,将返回1,否则返回0
LEVEL
From DEMO
Start With ID = '00001'
Connect By nocycle Prior ID =  PID
ORDER By DSC
--结果, 仅贴出部分数据(层级结构被破坏了)
00012   00001   贵州  1   2
00013   00001   河南  1   2
00011   00001   陕西  0   2
00111   00011   西安  1   3
00112   00011   咸阳  1   3
00113   00011   延安  1   3
00001   -1      中国  0   1
--ORDER SIBLINGS  By DSC
Select ID, PID, DSC,
connect_by_isleaf, -- 存在循环,将返回1,否则返回0
LEVEL
From DEMO
Start With ID = '00001'
Connect By nocycle Prior ID =  PID
ORDER SIBLINGS  By DSC
--结果(Level 层级不变)
00001   -1      中国  0   1
00012   00001   贵州  1   2
00013   00001   河南  1   2
00011   00001   陕西  0   2
00111   00011   西安  1   3
00112   00011   咸阳  1   3
00113   00011   延安  1   3

以上是关于start with ... connect by 递归查询的主要内容,如果未能解决你的问题,请参考以下文章

ORACLE:start with ... connect by prior

start with ... connect by 递归查询

oracle 多级菜单查询 。start with connect by prior

oracle树形查询 start with connect by

start with connect by prior 递归查询用法

Oracle中start with...connect by子句的用法