oracle层级查询connect by和postgres曾经查询with cte as

树形查询start with …connect by

在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

sql查询

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实现110的序列:

select level from dual connect by level<=10;

connect by例子2

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' );

对应B树结构为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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 cte as

简单的应用 with 辅助取值

 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)

示例2

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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值