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
posted @ 2017-12-05 00:54  别离的岁月  阅读(1112)  评论(0编辑  收藏  举报