zoukankan      html  css  js  c++  java
  • oracle递归查询

    oracle的递归查询

    最近在看公司的OA系统,OA系统中基本都会有节点树,其中对于树上的数据展示,就是用了数据库的递归查询,在这里总结下递归查询。

    现在存在如下的一棵树:

    不会画树,将就一下,该树对应下面创建的表数据。

    创建如下表:

    create table DG
    (
    	id         NUMBER not null,  --主键
    	parent_id  NUMBER,	-- 父节点
    	childer_id NUMBER	-- 当前节点,当时名字没区号,也表示当前的值,即树中的值
    )
    

    创建如下数据:(除去最后一条数据,其他的数据与树中的数据严格一致)

    insert into DG (id , childer_id)values (goods_seq.nextval , 6);
    insert into DG (id , Parent_Id , Childer_id) values(goods_seq.nextval,6,10);
    insert into DG (id , Parent_Id , Childer_Id) values(goods_seq.nextval,10,9);
    insert into DG (id , Parent_Id , Childer_Id) values(goods_seq.nextval,9,1);
    insert into DG (id , Parent_Id , Childer_Id) values(goods_seq.nextval,9,2);
    insert into DG (id , Parent_Id , Childer_Id) values(goods_seq.nextval,6,8);
    insert into DG (id , Parent_Id , Childer_Id) values(goods_seq.nextval,8,3);
    insert into DG (id , Parent_Id , Childer_Id) values(goods_seq.nextval,8,4);
    insert into DG (id , Parent_Id , Childer_Id) values(goods_seq.nextval,10,7);
    insert into DG (id , Parent_Id , Childer_Id) values(goods_seq.nextval,11,7);
    
    1. 查询表中的数据如下

      select * from DG
      
      	ID		PARENT_ID	CHILDER_ID
      1	91.00			6.00
      2	92.00	6.00	10.00
      3	93.00	10.00	9.00
      4	94.00	9.00	1.00
      5	95.00	9.00	2.00
      6	96.00	6.00	8.00
      7	97.00	8.00	3.00
      8	98.00	8.00	4.00
      9	99.00	10.00	7.00
      10	100.00	11.00	7.00
      

    查询出来的是全部的数据,但是最后一条数据是不在树这张图上的,因不存在childer_id为11的记录

    1. 使用递归查询,查找出树上的全部数据,不在树上的记录不显示,即某条记录的parent_id必须是另一条记录的childer_id,符合该条件的记录都显示出来。递归存在一个入口和一个出口,入口使用start with传入参数,出口就是某条记录不满足这样的条件了

      select * from DG start with childer_id = 6 connect by parent_id = prior childer_id;

      		ID	PARENT_ID	CHILDER_ID
       1	91		6
       2	96	6	8
       3	97	8	3
       4	98	8	4
       5	92	6	10
       6	99	10	7
       7	93	10	9
       8	94	9	1
       9	95	9	2
      

    与上面比较,不在树中的记录已经没有被显示出来了。
    根据记录的显示,我们可以看出来记录是按照深度遍历的顺序排序的,而不是按广度遍历的顺序排序的
    但是在深度遍历的时候,对于兄弟节点,先遍历哪个,其实是随机的,或者说是按照先插入的数据排在前面,后插入的数据排在后面。
    但是如果我们需要按照我们的规则对兄弟节点的顺序进行排序,这样的排序明显不会符合我们。
    这里我们假设根据childer_id的大小排序,使用如下SQL:

    1. 使用order by排序

      select * from DG start with childer_id = 6 connect by parent_id = prior childer_id order by childer_id;

      		ID	PARENT_ID	CHILDER_ID
       1	96	6	8
       2	92	6	10
       3	97	8	3
       4	98	8	4
       5	94	9	1
       6	95	9	2
       7	93	10	9
       8	99	10	7
       9	100	11	7
       10	91		6
      

    虽然按照parent_id来进行排序了,但是整个结果都是按照parent_id来排序了,不再满足深度遍历的顺序,也就是不分层次了,这样的结果明显不是我们想要的。其实除去不在树中的那条记录外,这样的排序时跟下面的排序一致的

    	select * from DG ORDER BY PARENT_ID;
    
    1. 仍然按照深度遍历的顺序排序,但是兄弟节点的排序,使用order sibings by

      select * from DG start with childer_id = 6 connect by parent_id = prior childer_id order siblings by childer_id;

      	ID	PARENT_ID	CHILDER_ID
      1	91		6
      2	96	6	8
      3	97	8	3
      4	98	8	4
      5	92	6	10
      6	99	10	7
      7	93	10	9
      8	94	9	1
      9	95	9	2
      

    观察整个表的记录,满足了深度遍历的排序,观察3,4 和6,7这两组记录发现兄弟节点的排序已经被我们控制了,用的就是order siblings by

    5.获取每条记录的根节点,使用conect_by_root(field)函数

    select DG.*, connect_by_root(childer_id) as childer_root , connect_by_root(parent_id) as parent_root , connect_by_root(id) as id_root from DG start with childer_id = 6 connect by  parent_id = prior childer_id
    
    ID	PARENT_ID	CHILDER_ID	CHILDER_ROOT	PARENT_ROOT	ID_ROOT
    	91		6	6  		91
    	96	6	8	6  		91
    	97	8	3	6  		91
    	98	8	4	6  		91
    	92	6	10	6  		91
    	99	10	7	6  		91
    	93	10	9	6  		91
    	94	9	1	6  		91
    	95	9	2	6  		91
    	--其中parent_root这一列是没有数据的,因为根节点对应的记录是没有parent_id值的
    

    这样就获取了每条记录的根节点。因为查询出来的记录都是在一棵树上的,所以肯定所有记录的根节点信息都是相同的,通常,我们都是查找某一条记录的根节点,只要加个条件就可以了,如下:

    select DG.*, connect_by_root(childer_id) as childer_root , connect_by_root(parent_id) as parent_root , connect_by_root(id) as id_root from DG where childer_id = 4 start with childer_id = 6 connect by  parent_id = prior childer_id 
    
    ID	PARENT_ID	CHILDER_ID	CHILDER_ROOT	PARENT_ROOT	ID_ROOT
    98	8	4	6	空	91
    

    6.获取记录的在树上的层次(用数字表示,第一层是1,level)和是否是叶子节点(0表示不是,1表示是,connect_by_isleaf)

    select id , parent_id , childer_id , level , connect_by_isleaf from DG start with childer_id = 6 connect by parent_id = prior childer_id;
    
       	ID	PARENT_ID	CHILDER_ID	LEVEL	CONNECT_BY_ISLEAF
    		1	91		6	1	0
    		2	96	6	8	2	0
    		3	97	8	3	3	1
    		4	98	8	4	3	1
    		5	92	6	10	2	0
    		6	99	10	7	3	1
    		7	93	10	9	3	0
    		8	94	9	1	4	1
    		9	95	9	2	4	1
    

    观察倒数两列的值,再与上面的树进行比较

    7.显示节点的遍历路径 sys_connect_by_path(field,string)

    select id , parent_id , childer_id , substring(sys_connect_by_path(childer_id, '->'),3) AS PATH from DG start with childer_id = 6  connect by parent_id = prior childer_id;
    
    
    ID	PARENT_ID	CHILDER_ID	PATH
    	1	91		6	6
    	2	96	6	8	6->8
    	3	97	8	3	6->8->3
    	4	98	8	4	6->8->4
    	5	92	6	10	6->10
    	6	99	10	7	6->10->7
    	7	93	10	9	6->10->9
    	8	94	9	1	6->10->9->1
    	9	95	9	2	6->10->9->2
    不积跬步无以至千里
  • 相关阅读:
    第十次Java作业
    第七周Java作业
    第十二周作业
    上机练习
    第十一周作业
    第十一周上机练习
    第十周上机练习
    第九周上机作业
    第八周作业
    第八周上机作业
  • 原文地址:https://www.cnblogs.com/jinxiuze/p/7979842.html
Copyright © 2011-2022 走看看