说明:
1. START WITH:告诉系统以哪个节点作为根结点开始查找并构造结果集,该节点即为返回记录中的最高节点。
2. CONNECT_BY_ROOT 返回当前节点的最顶端节点
3. CONNECT_BY_ISLEAF 判断是否为叶子节点,如果这个节点下面有子节点,则不为叶子节点
4. LEVEL 伪列表示节点深度,从1开始
5. SYS_CONNECT_BY_PATH函数显示详细路径
建表
createtable TREETABLE
(
ID VARCHAR2(10),
NAME VARCHAR2(10),
PARENT_ID VARCHAR2(10)
)
插入数据
insert into TREETABLE (ID, NAME, PARENT_ID)
values ('100000', '根节点', '');
insert into TREETABLE (ID, NAME, PARENT_ID)
values ('100100', '节点1', '100000');
insert into TREETABLE (ID, NAME, PARENT_ID)
values ('100101', '节点3','100100');
insert into TREETABLE (ID, NAME, PARENT_ID)
values ('100102', '节点4','100100');
insert into TREETABLE (ID, NAME, PARENT_ID)
values ('100200', '节点2','100000');
insert into TREETABLE (ID, NAME, PARENT_ID)
values ('100201', '节点5','100200');
insert into TREETABLE (ID, NAME, PARENT_ID)
values ('100202', '节点6','100200');
查询数据
select T.*
from TREETABLE T
orderby T.ID
ID |
NAME |
PARENT_ID |
100000 |
根节点 |
|
100100 |
节点1 |
100000 |
100101 |
节点3 |
100100 |
100102 |
节点4 |
100100 |
100200 |
节点2 |
100000 |
100201 |
节点5 |
100200 |
100202 |
节点6 |
100200 |
1、从根节点开始从上到下遍历访问
--注意:(1)、ISLEAF和LEVEL属性的值
--(2)、可能对应一个或多个分支
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
startwith T.ID = '100000'
connectby T.PARENT_ID= prior T.ID
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100000 |
100000 |
根节点 |
0 |
1 |
|
100000 |
100000 |
100100 |
**********节点1 |
0 |
2 |
100000 |
100100 |
100101 |
********************节点3 |
1 |
3 |
100000 |
100100 |
100102 |
********************节点4 |
1 |
3 |
100000 |
100000 |
100200 |
**********节点2 |
0 |
2 |
100000 |
100200 |
100201 |
********************节点5 |
1 |
3 |
100000 |
100200 |
100202 |
********************节点6 |
1 |
3 |
2、从节点100100开始从上到下遍历访问
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
startwith T.ID = '100100'
connectby T.PARENT_ID= prior T.ID
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100100 |
100000 |
100100 |
节点1 |
0 |
1 |
100100 |
100100 |
100101 |
**********节点3 |
1 |
2 |
100100 |
100100 |
100102 |
**********节点4 |
1 |
2 |
3、不指定START WITH
--如果不指定start with,则oracle会把所有的节点都当成根节点分别往下遍历。
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
connectby T.PARENT_ID= prior T.ID
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100100 |
100000 |
100100 |
节点1 |
0 |
1 |
100100 |
100100 |
100101 |
**********节点3 |
1 |
2 |
100100 |
100100 |
100102 |
**********节点4 |
1 |
2 |
100200 |
100000 |
100200 |
节点2 |
0 |
1 |
100200 |
100200 |
100201 |
**********节点5 |
1 |
2 |
100200 |
100200 |
100202 |
**********节点6 |
1 |
2 |
100101 |
100100 |
100101 |
节点3 |
1 |
1 |
100102 |
100100 |
100102 |
节点4 |
1 |
1 |
100201 |
100200 |
100201 |
节点5 |
1 |
1 |
100202 |
100200 |
100202 |
节点6 |
1 |
1 |
100000 |
100000 |
根节点 |
0 |
1 |
|
100000 |
100000 |
100100 |
**********节点1 |
0 |
2 |
100000 |
100100 |
100101 |
********************节点3 |
1 |
3 |
100000 |
100100 |
100102 |
********************节点4 |
1 |
3 |
100000 |
100000 |
100200 |
**********节点2 |
0 |
2 |
100000 |
100200 |
100201 |
********************节点5 |
1 |
3 |
100000 |
100200 |
100202 |
********************节点6 |
1 |
3 |
4、从节点100101开始从下到上访问到根节点
--注意:(1)、此时原来的根节点(100000)的ISLEAF是1,而原来的叶子节点(100102)的ISLEAF是0
--(2)、LEVEL层次也发生了变化
--(3)、这种情况下只可能有一个分支
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
startwith T.ID = '100102'
connectby T.ID = prior T.PARENT_ID
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100102 |
100100 |
100102 |
节点4 |
0 |
1 |
100102 |
100000 |
100100 |
**********节点1 |
0 |
2 |
100102 |
100000 |
********************根节点 |
1 |
3 |
4的变形
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (5 - level), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
startwith T.ID = '100102'
connectby T.ID = prior T.PARENT_ID
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100102 |
100100 |
100102 |
****************************************节点4 |
0 |
1 |
100102 |
100000 |
100100 |
******************************节点1 |
0 |
2 |
100102 |
100000 |
********************根节点 |
1 |
3 |
5、在1的sql中加入where level进行过滤
--可以看到,得到的结果的level值都是<=2的
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
wherelevel <=2
startwith T.ID = '100000'
connectby T.PARENT_ID= prior T.ID
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100000 |
100000 |
根节点 |
0 |
1 |
|
100000 |
100000 |
100100 |
**********节点1 |
0 |
2 |
100000 |
100000 |
100200 |
**********节点2 |
0 |
2 |
过滤个体和整个分支
6、在1的sql中加入where 过滤个体(从根到叶的遍历)
--可以看到,在结果中没有节点1(100100),但是他的子孙还在
--其实通过了解sql的执行顺序也不难理解这样的过滤个体
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
where t.id != '100100'
startwith T.ID = '100000'
connectby T.PARENT_ID= prior T.ID
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100000 |
100000 |
根节点 |
0 |
1 |
|
100000 |
100100 |
100101 |
********************节点3 |
1 |
3 |
100000 |
100100 |
100102 |
********************节点4 |
1 |
3 |
100000 |
100000 |
100200 |
**********节点2 |
0 |
2 |
100000 |
100200 |
100201 |
********************节点5 |
1 |
3 |
100000 |
100200 |
100202 |
********************节点6 |
1 |
3 |
7、在4的sql中加入where过滤个体(从叶到根的遍历)
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
where t.id != '100100'
startwith T.ID = '100102'
connectby T.ID = prior T.PARENT_ID
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100102 |
100100 |
100102 |
节点4 |
0 |
1 |
100102 |
100000 |
********************根节点 |
1 |
3 |
8、在1的sql中用connect by 过滤整个分支(从根到叶的遍历)
--使用connect by 子句不仅排除所提到的节点,而且排除他的所有子节点。
--connect by 子句实际上是对树结构进行追踪。如果没有100100节点,那么也不会有他的子节点
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
startwith T.ID = '100000'
connectby T.PARENT_ID= prior T.ID
and t.id != '100100'
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100000 |
100000 |
根节点 |
0 |
1 |
|
100000 |
100000 |
100200 |
**********节点2 |
0 |
2 |
100000 |
100200 |
100201 |
********************节点5 |
1 |
3 |
100000 |
100200 |
100202 |
********************节点6 |
1 |
3 |
9、在4的sql中用connect by 过滤整个分支(从叶到根的遍历)
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 20 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
from TREETABLE T
startwith T.ID = '100202'
connectby T.ID = prior T.PARENT_ID
and t.id != '100000'
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
100202 |
100200 |
100202 |
节点6 |
0 |
1 |
100202 |
100000 |
100200 |
**********节点2 |
1 |
2 |
跳出循环
--如果我们把根节点的parent_id修改成100102,即在树中形成了一个环
--此时,如果执行1的sql,会发现有错误 '用户数据中的connect by 循环'
--当您获得像这样的一条错误消息时,您可以使用 CONNECT_BY_ISCYCLE 虚拟列来确定引起问题的行的位置。
--要做到这一点,您还必须添加 NOCYCLE 关键字到 CONNECT BY 子句中,防止数据库进入层次结构中的任何循环
select CONNECT_BY_ROOT T.ID "ROOT"
,T.PARENT_ID
,T.ID
,LPAD('*', 10 * (level - 1), '*') || T.NAME asname
,CONNECT_BY_ISLEAF "ISLEAF"
,level
,CONNECT_BY_ISCYCLE "ISCYCLE"
from TREETABLE T
startwith T.ID = '100000'
connectbyNOCYCLE T.PARENT_ID = prior T.ID
ROOT |
PARENT_ID |
ID |
NAME |
ISLEAF |
LEVEL |
ISCYCLE |
100000 |
100102 |
100000 |
根节点 |
0 |
1 |
0 |
100000 |
100000 |
100100 |
**********节点1 |
0 |
2 |
0 |
100000 |
100100 |
100101 |
********************节点3 |
1 |
3 |
0 |
100000 |
100100 |
100102 |
********************节点4 |
1 |
3 |
1 |
100000 |
100000 |
100200 |
**********节点2 |
0 |
2 |
0 |
100000 |
100200 |
100201 |
********************节点5 |
1 |
3 |
0 |
100000 |
100200 |
100202 |
********************节点6 |
1 |
3 |
0 |
基本规则
使用connect by 和start with来创建类似于树的报表并不难,只要遵循以下基本规则即可
1、使用connect by 时各子句的顺序为:
(1)select
(2)from
(3)where
(4)start with
(5)connect by
(6)order by
2、prior强制报表的顺序变为从根到叶(如果prior列是父辈)或从叶到根(如果prior列是后代)
3、虽然where子句可以从树中排除个体,但不排除他们的子孙(或者祖先,如果是从叶到根的遍历)
4、connect by中的条件(尤其是不等于)可以排除个体和它所有的子孙(或祖先,取决于怎样跟踪树)
5、connect by不能和where子句中的表连接使用