如果表中存在层次数据,则可以使用层次化查询子句查询出表中行记录之间的层次关系
基本语法:
[ START WITH CONDITION1 ]
CONNECT BY [ NOCYCLE ] CONDITION2
示例:
select id,parentid,partname, level
from tabpart
start with id = 11
connect by nocycle prior id = parentid;
表中存在层次数据
数据之间的层次关系即父子关系,通过表中列与列间的关系来描述,如tabpart表中的id和parentid。id表示部门编号,parentid表示部门归属的更高层部门编号,在表的每一行中都有一个表示父节点的parentid(除根节点外),通过每个节点的父节点,就可以确定整个树结构。
CONNECT BY [ NOCYCLE ] CONDITION2 层次子句作用
CONDITION2 [PRIOR expr = expr] : 指定层次结构中父节点与子节点之之间的关系。
CONDITION2 中的 一元运算符 PRIORY 必须放置在连接关系的两列中某一个的前面。在连接关系中,除了可以使用列名外,还允许使用列表达式。
start with 子句为可选项,用来标识哪行作为查找树型结构的第一行(即根节点,可指定多个根节点)。若该子句被省略,则表示所有满足查询条件的行作为根节点。
-- sql 1
select id,parentid, level
from tabpart
start with parentid = 0
connect by prior id = parentid;
结果:
id parentid level
1 0 1
13 1 2
12 1 2
123 12 3
122 12 3
121 12 3
1213 121 4
1212 121 4
1211 121 4
11 1 2
113 11 3
112 11 3
111 11 3
分析
层次查询执行逻辑:
a.确定上一行(相对于步骤b中的当前行),若start with 子句存在,则以该语句确定的行为上一行,若不存在则将所有的数据行视为上一行。
b.从上一行出发,扫描除该行之外所有数据行。
c.匹配条件 prior id = parentid
注意 一元运算符 prior,意思是之前的,指上一行
当前行定义:步骤b中扫描得到的所有行中的某一行
匹配条件含义:当前行字段 parentid 的值等于上一行字段id中的值,若满足则取出该行,并将level + 1,匹配完所有行记录后,将满足条件的行作为上一行,执行步骤 b,c。直到所有行匹配结束。
--sql 2
select id,parentid, level
from tabpart
start with id = 1211
connect by prior parentid = id;
结果:
id parentid level
1211 121 1
121 12 2
12 1 3
1 0 4
分析
层次查询执行逻辑:
a.确定上一行(相对于步骤b中的当前行),若start with 子句存在,则以该语句确定的行为上一行,若不存在则将所有的数据行视为上一行。
b.从上一行出发,扫描除该行之外所有数据行。
c.匹配条件 parentid = id
注意 一元运算符 prior,意思是之前的,指上一行
当前行定义:步骤b中扫描得到的所有行中的某一行
匹配条件含义:当前行字段 id 的值等于上一行字段 parentid 中的值,若满足则取出该行,并将 level + 1,匹配完所有行记录后,将满足条件的行作为上一行,执行步骤 b,c。直到所有行匹配结束。
自顶向下,自下向上口诀:
start with child_id = 10 connect by [prior] child_id = [prior] parent_id
prior 和子列在一起,表示寻找它的子孙,即自顶向下;
-- 向下
select id,parentid, level
from tabpart
start with id = 12
connect by prior id = parentid;
prior 和父列在一起,表示开始寻找它的爸爸,即自下向上;
-- 向上
select id,parentid, level
from tabpart
start with id = 12
connect by id = prior parentid;
指示该层级之内递归查询的哪一步骤返回了行。即从start with的条件开始为第一层,每一迭代加一层。示例如上。
指示一行是否有一个还是其祖先的孩子。存在循环时,当出现该情况时,connect by子句应该加上NOCYCLE。
示例:
表中增加一行数据,使符合循环的要求,执行sql 1
-- sql 1
select id,parentid, level
from tabpart
start with parentid = 0
connect by prior id = parentid;
将报错:
26079: CONNECT BY 查询已导致循环。
加上NOCYCLE
select id,parentid, level, CONNECT_BY_ISCYCLE
from tabpart
start with parentid = 0
connect by nocycle prior id = parentid;
结果:
id parentid level connect_by_iscycle
1 0 1 0
13 1 2 0
12 1 2 0
123 12 3 0
122 12 3 0
121 12 3 0
1213 121 4 0
1212 121 4 0
1211 121 4 0
0 1211 5 1
11 1 2 0
113 11 3 0
112 11 3 0
111 11 3 0
指示一行在查询返回的行之中是否有任何的孩子。如果是leaf(叶子),返回1,否则返回0。
示例:
select id,parentid, level, CONNECT_BY_ISLEAF
from tabpart
start with parentid = 0
connect by prior id = parentid;
结果:
id parentid level connect_by_isleaf
1 0 1 0
13 1 2 1
12 1 2 0
123 12 3 1
122 12 3 1
121 12 3 0
1213 121 4 1
1212 121 4 1
1211 121 4 0
0 1211 5 0
11 1 2 0
113 11 3 1
112 11 3 1
111 11 3 1
可构建和返回一字符串,该字符串表示从指定的行到层级的根的路径。类似于wm_concat函数,按层次顺序拼接字段。
示例:
select id,parentid, level, SYS_CONNECT_BY_PATH(partname,'/')
from tabpart
start with parentid = 0
connect by nocycle prior id = parentid;
结果:
id parentid level expression_1
1 0 1 /总部
13 1 2 /总部/华东区
12 1 2 /总部/华南区
123 12 3 /总部/华南区/海南
122 12 3 /总部/华南区/广西
121 12 3 /总部/华南区/广东
1213 121 4 /总部/华南区/广东/佛山
1212 121 4 /总部/华南区/广东/深圳
1211 121 4 /总部/华南区/广东/广州
0 1211 5 /总部/华南区/广东/广州/天河
11 1 2 /总部/华北区
113 11 3 /总部/华北区/天津
112 11 3 /总部/华北区/河北
111 11 3 /总部/华北区/北京
对返回的每个级别的同一父母的兄弟行进行排序。
示例:
select id,parentid, level, partname
from tabpart
start with id = 1
connect by nocycle prior id = parentid
ORDER SIBLINGS BY id;
为其运算对象的根祖先返回一表达式。
示例:
select id,parentid, level, CONNECT_BY_ROOT partname
from tabpart
start with parentid = 0
connect by nocycle prior id = parentid;
结果:
id parentid level partname
1 0 1 总部
13 1 2 总部
12 1 2 总部
123 12 3 总部
122 12 3 总部
121 12 3 总部
1213 121 4 总部
1212 121 4 总部
1211 121 4 总部
0 1211 5 总部
11 1 2 总部
113 11 3 总部
112 11 3 总部
111 11 3 总部
引用从前一递归步骤返回的值(此处的“步骤”是指该递归查询的一次迭代)。
参见第二部分。
递归查询路径
select id,parentid,partname, level
from tabpart
start with id = 1212
connect by nocycle id = prior parentid
order by level desc;
结果:
id parentid partnum level
1 0 总部 4
12 1 华南区 3
121 12 广东 2
1211 121 广州 1
自下而上,使用SYS_CONNECT_BY_PATH,获取的结果并不是需要的自上而下的顺序。这里可以使用wm_concat来拼接。
select replace(wm_concat(partname),',',' -> ')
from (
select id,parentid, partname, level
from tabpart
start with id = 1211
connect by nocycle id = prior parentid
order by level desc
);
结果:
(expression) 总部 -> 华南区 -> 广东 -> 广州
涉及到的表结构及语句
drop table if exists tabpart;
create table tabpart(id int, parentid int, partname varchar(200),primary key(id));
insert into tabpart values(1,0,'总部');
insert into tabpart values(11,1,'华北区');
insert into tabpart values(12,1,'华南区');
insert into tabpart values(13,1,'华东区');
insert into tabpart values(111,11,'北京');
insert into tabpart values(112,11,'河北');
insert into tabpart values(113,11,'天津');
insert into tabpart values(121,12,'广东');
insert into tabpart values(122,12,'广西');
insert into tabpart values(123,12,'海南');
insert into tabpart values(1211,121,'广州');
insert into tabpart values(1212,121,'深圳');
insert into tabpart values(1213,121,'佛山');
-- 增加一行,用于循环
insert into tabpart values(0,1211,'天河区');
-- 修改为正确值
update tabpart set id = 12111 where id = 0;