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