赞
踩
在oracle中start with connect by (prior) 用来对树形结构的数据进行查询。其中start with conditon 给出的是数据搜索范围, connect by后面给出了递归查询的条件,prior 关键字表示父数据,prior 条件表示子数据需要满足父数据的什么条件。如下
start with id= ‘10001’ connect by prior parent_id= id and prior num = 5
表示查询id为10001,并且递归查询parent_id=id,为5的记录。
Oracle中start with和connect by 用法理解
connect by 是结构化查询中用到的,其基本语法是:
1 select … from tablename
2 start with 条件1
3 connect by 条件2
4 where 条件3;
1.
2.
3.
4.
例:
1 select * from table
2 start with org_id = ‘HBHqfWGWPy’
3 connect by prior org_id = parent_id;
1.
2.
3.
简单说来是将一个树状结构存储在一张表里,比如一个表中存在两个字段:org_id,parent_id,那么通过表示每一条记录的parent是谁,就可以形成一个树状结构,用上述语法的查询可以取得这棵树的所有记录,其中:
条件1 是根结点的限定语句,当然可以放宽限定条件,以取得多个根结点,实际就是多棵树。
条件2 是连接条件,其中用PRIOR表示上一条记录,比如 CONNECT BY PRIOR org_id = parent_id;就是说上一条记录的org_id 是本条记录的parent_id,即本记录的父亲是上一条记录。
条件3 是过滤条件,用于对返回的所有记录进行过滤。
简单介绍如下:
在扫描树结构表时,需要依此访问树结构的每个节点,一个节点只能访问一次,其访问的步骤如下:
第一步:从根节点开始;
第二步:访问该节点;
第三步:判断该节点有无未被访问的子节点,若有,则转向它最左侧的未被访问的子节,并执行第二步,否则执行第四步;
第四步:若该节点为根节点,则访问完毕,否则执行第五步;
第五步:返回到该节点的父节点,并执行第三步骤。
总之:扫描整个树结构的过程也即是中序遍历树的过程。
1.树结构的描述
树结构的数据存放在表中,数据之间的层次关系即父子关系,通过表中的列与列间的关系来描述,如EMP表中的EMPNO和MGR,EMPNO表示该雇员的编号,MGR表示领导该雇员的人的编号,即子节点的MGR值等于父节点的EMPNO值。在表的每一行中都有一个表示父节点的MGR(除根节点外),通过每个节点的父节点,就可以确定整个树结构。
在SELECT命令中使用CONNECT BY 和START WITH 子句可以查询表中的树型结构关系,其命令格式如下:
SELECT . . .
CONNECT BY {PRIOR 列名1=列名2|列名1=PRIOR 裂名2}
[START WITH];
其中:CONNECT BY子句说明每行数据将是按层次顺序检索,并规定将表中的数据连入树型结构的关系中。PRIOR运算符必须放置在连接关系的两列中某一个的前面。对于节点间的父子关系,PRIOR运算符在一侧表示父节点,在另一侧表示子节点,从而确定查找树结构是的顺序是自顶向下还是自底向上。
在连接关系中,除了可以使用列名外,还允许使用列表达式。START WITH 子句为可选项,用来标识哪个节点作为查找树型结构的根节点,若该子句被省略,则表示所有满足查询条件的行作为根节点。
START WITH:不但可以指定一个根节点,还可以指定多个根节点。
2.关于PRIOR
运算符PRIOR被放置于等号前后的位置,决定着查询时的检索顺序。
PRIOR被置于CONNECT BY子句中等号的前面时,则强制从根节点到叶节点的顺序检索,即由父节点向子节点方向通过树结构,我们称之为自顶向下的方式。如:
CONNECT BY PRIOR EMPNO=MGR
PIROR运算符被置于CONNECT BY 子句中等号的后面时,则强制从叶节点到根节点的顺序检索,即由子节点向父节点方向通过树结构,我们称之为自底向上的方式。例如:
CONNECT BY EMPNO=PRIOR MGR
在这种方式中也应指定一个开始的节点。
3.定义查找起始节点
在自顶向下查询树结构时,不但可以从根节点开始,还可以定义任何节点为起始节点,以此开始向下查找。这样查找的结果就是以该节点为开始的结构树的一枝。
4.使用LEVEL
在具有树结构的表中,每一行数据都是树结构中的一个节点,由于节点所处的层次位置不同,所以每行记录都可以有一个层号。层号根据节点与根节点的距离确定。不论从哪个节点开始,该起始根节点的层号始终为1,根节点的子节点为2, 依此类推。图1.2就表示了树结构的层次。
5.节点和分支的裁剪
在对树结构进行查询时,可以去掉表中的某些行,也可以剪掉树中的一个分支,使用WHERE子句来限定树型结构中的单个节点,以去掉树中的单个节点,但它却不影响其后代节点(自顶向下检索时)或前辈节点(自底向顶检索时)。
6.排序显示
像在其它查询中一样,在树结构查询中也可以使用ORDER BY 子句,改变查询结果的显示顺序,而不必按照遍历树结构的顺序。
7. 实例
oracle 提供了start with connect by 语法结构可以实现递归查询。
-----------------------------------
Oracle中start with和connect by 用法理解
https://blog.51cto.com/u_15127632/3437501
-- 表结构
create table menu(
id varchar2(64) not null,
parent_id varchar2(64) not null,
name varchar2(100) not null,
depth number(2) not null,
primary key (id)
)
-- 初始化数据
-- 顶级菜单
insert into menu values ('100000', '0', '顶级菜单1', 1);
insert into menu values ('200000', '0', '顶级菜单2', 1);
insert into menu values ('300000', '0', '顶级菜单3', 1);
-- 父级菜单
-- 顶级菜单1 直接子菜单
insert into menu values ('110000', '100000', '菜单11', 2);
insert into menu values ('120000', '100000', '菜单12', 2);
insert into menu values ('130000', '100000', '菜单13', 2);
insert into menu values ('140000', '100000', '菜单14', 2);
-- 顶级菜单2 直接子菜单
insert into menu values ('210000', '200000', '菜单21', 2);
insert into menu values ('220000', '200000', '菜单22', 2);
insert into menu values ('230000', '200000', '菜单23', 2);
-- 顶级菜单3 直接子菜单
insert into menu values ('310000', '300000', '菜单31', 2);
-- 菜单13 直接子菜单
insert into menu values ('131000', '130000', '菜单131', 3);
insert into menu values ('132000', '130000', '菜单132', 3);
insert into menu values ('133000', '130000', '菜单133', 3);
-- 菜单132 直接子菜单
insert into menu values ('132100', '132000', '菜单1321', 4);
insert into menu values ('132200', '132000', '菜单1332', 4);
### 生成的菜单层次结构如下:
顶级菜单1
菜单11
菜单12
菜单13
菜单131
菜单132
菜单1321
菜单1322
菜单133
菜单14
顶级菜单2
菜单21
菜单22
菜单23
顶级菜单3
菜单31
col NAME format a50;
col ID format a50;
col EPTH format a50;
col PARENT_ID format a50;
--prior放的左右位置决定了检索是自底向上还是自顶向下. 左边是自上而下(找子节点),右边是自下而上(找父节点)
--###prior在左边还是右边的理论是错的,应该是如下解释:
start with 子句:遍历起始条件,有个小技巧,如果要查父结点,这里可以用子结点的列,反之亦然。
connect by 子句:连接条件。关键词prior,prior跟父节点列parentid放在一起,就是往父结点方向遍历;prior跟子结点列subid放在一起,则往叶子结点方向遍历,
parentid、subid两列谁放在“=”前都无所谓,关键是prior跟谁在一起。
order by 子句:排序,不用多说。
--比如找子节点:
C##SCOTT@LHRCDB> select * from menu start with id='200000' connect by prior id = parent_id ;
ID PARENT_ID NAME DEPTH
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ----------
200000 0 顶级菜单2 1
210000 200000 菜单21 2
220000 200000 菜单22 2
230000 200000 菜单23 2
C##SCOTT@LHRCDB> select * from menu start with id='200000' connect by parent_id=prior id;
ID PARENT_ID NAME DEPTH
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ----------
200000 0 顶级菜单2 1
210000 200000 菜单21 2
220000 200000 菜单22 2
230000 200000 菜单23 2
C##SCOTT@LHRCDB>
--找父节点
select * from menu start with id='130000' connect by id = prior parent_id;
--找子节点节点
-- (子节点)id为130000的菜单,以及130000菜单下的所有直接或间接子菜单(prior 在左边, prior、parent_id(等号右边)在右边)
select * from menu start with id='130000' connect by prior id = parent_id ;
select * from menu start with id='200000' connect by parent_id=prior id;
-- (父节点)id为1321的菜单,以及1321菜单下的所有直接或间接父菜单(prior、parent_id(等号左边) 都在左边)
select * from menu start with id='132100' connect by prior parent_id = id;
-- prior 后面跟的是(parent_id) 则是查找父节点,prior后面跟的是(id)则是查找子节点
--根据菜单组分类统计每个菜单包含子菜单的个数
select id, max(name) name, count(1) from menu
group by id
connect by prior parent_id = id
order by id
-- 查询所有的叶子节点
select t2.* from menu t2 where id not in(select t.parent_id from menu t) order by id;
--生成数字序列结果集
select rownum from dual connect by rownum<=10;
使用level实现1到10的序列:
select level from dual connect by level<=10;
create table a_test
( parentid varchar2(10),
subid varchar2(10));
insert into a_test values ( '1', '2' );
insert into a_test values ( '1', '3' );
insert into a_test values ( '2', '4' );
insert into a_test values ( '2', '5' );
insert into a_test values ( '3', '6' );
insert into a_test values ( '3', '7' );
insert into a_test values ( '5', '8' );
insert into a_test values ( '5', '9' );
insert into a_test values ( '7', '10' );
insert into a_test values ( '7', '11' );
insert into a_test values ( '10', '12' );
insert into a_test values ( '10', '13' );
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-COFHhqy7-1691146268944)(media/connect_by)]
接下来看一个示例:
要求给出其中一个结点值,求其最终父结点。以7为例,看一下代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ITo25MJ-1691146268945)(media/2)]
start with 子句:遍历起始条件,有个小技巧,如果要查父结点,这里可以用子结点的列,反之亦然。
connect by 子句:连接条件。关键词prior,prior跟父节点列parentid放在一起,就是往父结点方向遍历;prior跟子结点列subid放在一起,则往叶子结点方向遍历,
parentid、subid两列谁放在“=”前都无所谓,关键是prior跟谁在一起。
order by 子句:排序,不用多说。
--------------------------------------------------
下面看看往叶子结点遍历的例子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKG60fO3-1691146268946)(media/3)]
这里start with 子句用了parentid列,具体区别后面举例说明。
connect by 子句中,prior跟subid在同一边,就是往叶子结点方向遍历去了。因为7有两个子结点,所以第一级中有两个结果(10和11),10有两个子结点(12,13),11无,所以第二级也有两个结果(12,13)。即12,13就是叶子结点。
下面看下start with子句中选择不同的列的区别:
以查询叶子结点(往下遍历)为例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tVn8WEiq-1691146268946)(media/4)]
结果很明显,原意是要以7为父结点,遍历其子结点,左图取的是父结点列的值,结果符合原意;右图取的是子结点列的值,结果多余的显示了7 的父结点3.
---------------------------------------
关于where条件的语句,以后验证后再记录。先留个疑问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3nw70XAQ-1691146268946)(media/5)]
WITH t as (
SELECT generate_series(1,3)
) SELECT * from t;
--执行结果:
wind=# WITH t as (
wind(# SELECT generate_series(1,3)
wind(# ) SELECT * from t;
generate_series
-----------------
1
2
3
(3 rows)
wind=#
WITH RECURSIVE t(x) as (
SELECT 1
UNION
SELECT x + 1
FROM t
WHERE x < 3 --这里的where条件是对常量的限制
)
SELECT x FROM t;
--执行结果:
wind=# WITH RECURSIVE t(x) as (
wind(# SELECT 1
wind(# UNION
wind(# SELECT x + 1
wind(# FROM t
wind(# WHERE x < 3
wind(# )
wind-# SELECT x FROM t;
x
---
1
2
3
(3 rows)
wind=#
--xtp支持语法:
WITH RECURSIVE t(x) AS (
VALUES (1)
UNION
SELECT x+1 FROM t WHERE x < 3
)
SELECT * FROM t;
kingledb=> WITH RECURSIVE t(x) as (
kingledb(> SELECT 1
kingledb(> UNION
kingledb(> SELECT x + 1
kingledb(> FROM t
kingledb(> WHERE x < 5
kingledb(> )
kingledb-> SELECT sum(x) FROM t;
sum
-----
15
(1 row)
Time: 1.249 ms
-- 经典的递归查询取和
-----------------------------------
©著作权归作者所有:来自51CTO博客作者kingle_work的原创作品,请联系作者获取转载授权,否则将追究法律责任
postgres WITH 查询
https://blog.51cto.com/kingle/4908276
create table tb(id varchar(3) , pid varchar(3) , name varchar(10));
insert into tb values('002' , 0 , '浙江省');
insert into tb values('001' , 0 , '广东省');
insert into tb values('003' , '002' , '衢州市');
insert into tb values('004' , '002' , '杭州市') ;
insert into tb values('005' , '002' , '湖州市');
insert into tb values('006' , '002' , '嘉兴市') ;
insert into tb values('007' , '002' , '宁波市');
insert into tb values('008' , '002' , '绍兴市') ;
insert into tb values('009' , '002' , '台州市');
insert into tb values('010' , '002' , '温州市') ;
insert into tb values('011' , '002' , '丽水市');
insert into tb values('012' , '002' , '金华市') ;
insert into tb values('013' , '002' , '舟山市');
insert into tb values('014' , '004' , '上城区') ;
insert into tb values('015' , '004' , '下城区');
insert into tb values('016' , '004' , '拱墅区') ;
insert into tb values('017' , '004' , '余杭区') ;
insert into tb values('018' , '011' , '金东区') ;
insert into tb values('019' , '001' , '广州市') ;
insert into tb values('020' , '001' , '深圳市') ;
with RECURSIVE cte as
(
select a.id,cast(a.name as varchar(100)) from tb a where id='002'
union all
select k.id,cast(c.name||'>'||k.name as varchar(100)) as name from tb k inner join cte c on c.id = k.pid
)select id,name from cte ;
--执行结果
qbadmin@10.14.41.157:20158/wind> with RECURSIVE cte as
-> (
-> select a.id,cast(a.name as varchar(100)) from tb a where id='002'
-> union all
-> select k.id,cast(c.name||'>'||k.name as varchar(100)) as name from tb k inner join cte c on c.id = k.pid
-> )select id,name from cte ;
id | name
------+-----------------------
002 | 浙江省
003 | 浙江省>衢州市
004 | 浙江省>杭州市
005 | 浙江省>湖州市
006 | 浙江省>嘉兴市
007 | 浙江省>宁波市
008 | 浙江省>绍兴市
009 | 浙江省>台州市
010 | 浙江省>温州市
011 | 浙江省>丽水市
012 | 浙江省>金华市
013 | 浙江省>舟山市
014 | 浙江省>杭州市>上城区
015 | 浙江省>杭州市>下城区
016 | 浙江省>杭州市>拱墅区
017 | 浙江省>杭州市>余杭区
018 | 浙江省>丽水市>金东区
(17 rows)
Time: 4ms total (execution 3ms / network 0ms)
qbadmin@10.14.41.157:20158/wind>
with RECURSIVE cte as
(
select a.id,cast(a.name as varchar(100)) from tb a where id='002'
union all
select k.id,cast(k.name||'>'||c.name as varchar(100)) as name from tb k inner join cte c on c.id = k.pid
)select id,name from cte ;
--执行sql
qbadmin@10.14.41.157:20158/wind> with RECURSIVE cte as
-> (
-> select a.id,cast(a.name as varchar(100)) from tb a where id='002'
-> union all
-> select k.id,cast(k.name||'>'||c.name as varchar(100)) as name from tb k inner join cte c on c.id = k.pid
-> )select id,name from cte ;
id | name
------+-----------------------
002 | 浙江省
003 | 衢州市>浙江省
004 | 杭州市>浙江省
005 | 湖州市>浙江省
006 | 嘉兴市>浙江省
007 | 宁波市>浙江省
008 | 绍兴市>浙江省
009 | 台州市>浙江省
010 | 温州市>浙江省
011 | 丽水市>浙江省
012 | 金华市>浙江省
013 | 舟山市>浙江省
014 | 上城区>杭州市>浙江省
015 | 下城区>杭州市>浙江省
016 | 拱墅区>杭州市>浙江省
017 | 余杭区>杭州市>浙江省
018 | 金东区>丽水市>浙江省
(17 rows)
Time: 10ms total (execution 10ms / network 1ms)
CREATE TABLE test_area(id int4,name varchar(32),fatherid int4);
INSERT INTO test_area VALUES (1, '中国',0);
INSERT INTO test_area VALUES (2,'辽宁',1);
INSERT INTO test_area VALUES (3, '山东',1);
INSERT INTO test_area VALUES (4,'沈阳',2);
INSERT INTO test_area VALUES (5, '大连',2);
INSERT INTO test_area VALUES (6,'济南',3);
INSERT INTO test_area VALUES (7,'和平区',4);
INSERT INTO test_area VALUES (8,'沈河区',4);
WITH RECURSIVE r as (
SELECT * from test_area WHERE id = 5
UNION
SELECT test_area.* from test_area , r WHERE test_area.id = r.fatherid
) SELECT * from r ORDER BY ID;
-----------------------------------
©著作权归作者所有:来自51CTO博客作者kingle_work的原创作品,请联系作者获取转载授权,否则将追究法律责任
postgres WITH 查询
https://blog.51cto.com/kingle/4908276
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。