oracle树查询的最重要的就是select…start with…connect by…prior语法了。依托于该语法。我们能够将一个表形结构的以树的顺序列出来。

在以下列述了oracle中树型查询的经常使用查询方式以及经常使用的与树查询相关的oracle特性函数等,在这里仅仅涉及到一张表中的树查询方式而不涉及多表中的关联等。

1、准备測试表和測试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
--菜单文件夹结构表
create table tb_menu(
  
id     number(10)
not null,
--主键id
  
title  varchar2(50),
--标题
  
parent number(10)
--parent
id
)
 
--父菜单
insert into tb_menu(id,
title, parent)
values(1,
'父菜单1',null);
insert into tb_menu(id,
title, parent)
values(2,
'父菜单2',null);
insert into tb_menu(id,
title, parent)
values(3,
'父菜单3',null);
insert into tb_menu(id,
title, parent)
values(4,
'父菜单4',null);
insert into tb_menu(id,
title, parent)
values(5,
'父菜单5',null);
--一级菜单
insert into tb_menu(id,
title, parent)
values(6,
'一级菜单6',1);
insert into tb_menu(id,
title, parent)
values(7,
'一级菜单7',1);
insert into tb_menu(id,
title, parent)
values(8,
'一级菜单8',1);
insert into tb_menu(id,
title, parent)
values(9,
'一级菜单9',2);
insert into tb_menu(id,
title, parent)
values(10,
'一级菜单10',2);
insert into tb_menu(id,
title, parent)
values(11,
'一级菜单11',2);
insert into tb_menu(id,
title, parent)
values(12,
'一级菜单12',3);
insert into tb_menu(id,
title, parent)
values(13,
'一级菜单13',3);
insert into tb_menu(id,
title, parent)
values(14,
'一级菜单14',3);
insert into tb_menu(id,
title, parent)
values(15,
'一级菜单15',4);
insert into tb_menu(id,
title, parent)
values(16,
'一级菜单16',4);
insert into tb_menu(id,
title, parent)
values(17,
'一级菜单17',4);
insert into tb_menu(id,
title, parent)
values(18,
'一级菜单18',5);
insert into tb_menu(id,
title, parent)
values(19,
'一级菜单19',5);
insert into tb_menu(id,
title, parent)
values(20,
'一级菜单20',5);
--二级菜单
insert into tb_menu(id,
title, parent)
values(21,
'二级菜单21',6);
insert into tb_menu(id,
title, parent)
values(22,
'二级菜单22',6);
insert into tb_menu(id,
title, parent)
values(23,
'二级菜单23',7);
insert into tb_menu(id,
title, parent)
values(24,
'二级菜单24',7);
insert into tb_menu(id,
title, parent)
values(25,
'二级菜单25',8);
insert into tb_menu(id,
title, parent)
values(26,
'二级菜单26',9);
insert into tb_menu(id,
title, parent)
values(27,
'二级菜单27',10);
insert into tb_menu(id,
title, parent)
values(28,
'二级菜单28',11);
insert into tb_menu(id,
title, parent)
values(29,
'二级菜单29',12);
insert into tb_menu(id,
title, parent)
values(30,
'二级菜单30',13);
insert into tb_menu(id,
title, parent)
values(31,
'二级菜单31',14);
insert into tb_menu(id,
title, parent)
values(32,
'二级菜单32',15);
insert into tb_menu(id,
title, parent)
values(33,
'二级菜单33',16);
insert into tb_menu(id,
title, parent)
values(34,
'二级菜单34',17);
insert into tb_menu(id,
title, parent)
values(35,
'二级菜单35',18);
insert into tb_menu(id,
title, parent)
values(36,
'二级菜单36',19);
insert into tb_menu(id,
title, parent)
values(37,
'二级菜单37',20);
--三级菜单
insert into tb_menu(id,
title, parent)
values(38,
'三级菜单38',21);
insert into tb_menu(id,
title, parent)
values(39,
'三级菜单39',22);
insert into tb_menu(id,
title, parent)
values(40,
'三级菜单40',23);
insert into tb_menu(id,
title, parent)
values(41,
'三级菜单41',24);
insert into tb_menu(id,
title, parent)
values(42,
'三级菜单42',25);
insert into tb_menu(id,
title, parent)
values(43,
'三级菜单43',26);
insert into tb_menu(id,
title, parent)
values(44,
'三级菜单44',27);
insert into tb_menu(id,
title, parent)
values(45,
'三级菜单45',28);
insert into tb_menu(id,
title, parent)
values(46,
'三级菜单46',28);
insert into tb_menu(id,
title, parent)
values(47,
'三级菜单47',29);
insert into tb_menu(id,
title, parent)
values(48,
'三级菜单48',30);
insert into tb_menu(id,
title, parent)
values(49,
'三级菜单49',31);
insert into tb_menu(id,
title, parent)
values(50,
'三级菜单50',31);
commit;
 
select *
from tb_menu;

parent字段存储的是上级id,假设是顶级父节点。该parent为null(得补充一句。当初的确是这样设计的,只是如今知道,表中最好别有null记录。这会引起全文扫描,建议改成0取代)。

2、树操作

我们从最主要的操作,逐步列出树查询中常见的操作。全部查询出来的节点以家族中的辈份作例如。

1)、查找树中的全部顶级父节点(辈份最长的人)。

如果这个树是个文件夹结构。那么第一个操作总是找出全部的顶级节点,再依据该节点找到其下属节点。

1
select
* from tb_menu m where m.parent is
null;

2)、查找一个节点的直属子节点(全部儿子)。 假设查找的是直属子类节点,也是不用用到树型查询的。

1
select
* from tb_menu m where m.parent=
1;

3)、查找一个节点的全部直属子节点(全部后代)。

1
select
* from tb_menu m start with m.id=
1 connect
by m.parent=prior m.id;

这个查找的是id为1的节点下的全部直属子类节点,包含子辈的和孙子辈的全部直属节点。

4)、查找一个节点的直属父节点(父亲)。 假设查找的是节点的直属父节点。也是不用用到树型查询的。

1
2
3
4
--c-->child,
p->parent
select
c.id, c.title, p.id parent_id, p.title parent_title
from
tb_menu c, tb_menu p
where
c.parent=p.id and c.id=
6

5)、查找一个节点的全部直属父节点(祖宗)。

1
select
* from tb_menu m start with m.id=
38 connect
by prior m.parent=m.id;

这里查找的就是id为1的全部直属父节点。打个例如就是找到一个人的父亲、祖父等。可是值得注意的是这个查询出来的结果的顺序是先列出子类节点再列出父类节点,姑且觉得是个倒序吧。

上面列出两个树型查询方式,第3条语句和第5条语句。这两条语句之间的差别在于priorkeyword的位置不同。所以决定了查询的方式不同。 当parent = prior id时。数据库会依据当前的id迭代出parent与该id同样的记录,所以查询的结果是迭代出了全部的子类记录;而prior parent = id时。数据库会跟据当前的parent来迭代出与当前的parent同样的id的记录。所以查询出来的结果就是全部的父类结果。

下面是一系列针对树结构的更深层次的查询,这里的查询不一定是最优的查询方式。也许仅仅是当中的一种实现而已。

6)、查询一个节点的兄弟节点(亲兄弟)。

1
2
3
--m.parent=m2.parent-->同一个父亲
select
* from tb_menu m
where
exists (select * from tb_menu m2 where m.parent=m2.parent and m2.id=
6)

7)、查询与一个节点同级的节点(族兄弟)。

假设在表中设置了级别的字段。那么在做这类查询时会非常轻松,同一级别的就是与那个节点同级的,在这里列出不使用该字段时的实现!

1
2
3
4
5
6
7
8
with
tmp as(
      select
a.*, level leaf       
      from
tb_menu a               
      start
with a.parent is
null     
      connect
by a.parent = prior a.id)
select
*                              
from
tmp                            
where
leaf = (select leaf from tmp where id =
50);

这里使用两个技巧,一个是使用了level来标识每一个节点在表中的级别。还有就是使用with语法模拟出了一张带有级别的暂时表。

8)、查询一个节点的父节点的的兄弟节点(伯父与叔父)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
with
tmp as(
    select
tb_menu.*, level lev
    from
tb_menu
    start
with parent is
null
    connect
by parent = prior id)
    
select
b.*
from
tmp b,(select *
            from
tmp
            where
id =
21 and
lev =
2)
a
where
b.lev =
1
 
union
all
 
select
*
from
tmp
where
parent = (select distinct x.id
                from
tmp x, --祖父
                     tmp
y, --父亲
                     (select
*
                      from
tmp
                      where
id =
21 and
lev >
2)
z --儿子
                where
y.id = z.parent and x.id = y.parent);

这里查询分成下面几步。

首先,将第7个一样,将全表都使用暂时表加上级别。

其次,依据级别来推断有几种类型,以上文中举的样例来说。有三种情况:

(1)当前节点为顶级节点。即查询出来的lev值为1,那么它没有上级节点,不予考虑。

(2)当前节点为2级节点,查询出来的lev值为2。那么就仅仅要保证lev级别为1的就是其上级节点的兄弟节点。

(3)其他情况就是3以及以上级别。那么就要选查询出来其上级的上级节点(祖父),再来推断祖父的下级节点都是属于该节点的上级节点的兄弟节点。

最后,就是使用union将查询出来的结果进行结合起来。形成结果集。

9)、查询一个节点的父节点的同级节点(族叔)。

这个事实上跟第7种情况是同样的。

1
2
3
4
5
6
7
8
with
tmp as(
      select
a.*, level leaf       
      from
tb_menu a               
      start
with a.parent is
null     
      connect
by a.parent = prior a.id)
select
*                              
from
tmp                            
where
leaf = (select leaf from tmp where id =
6)
-
1;

基本上,常见的查询在里面了,不常见的也有部分了。当中。查询的内容都是节点的基本信息,都是数据表中的基本字段,可是在树查询中还有些特殊需求,是对查询数据进行了处理的。常见的包含列出树路径等。

补充一个概念。对于数据库来说,根节点并不一定是在数据库中设计的顶级节点,对于数据库来说。根节点就是start with開始的地方。

以下列出的是一些与树相关的特殊需求。

10)、名称要列出名称所有路径。

这里常见的有两种情况,一种是从顶级列出,直到当前节点的名称(或者其他属性);一种是从当前节点列出,直到顶级节点的名称(或其他属性)。

举地址为例:国内的习惯是从省開始、到市、到县、到居委会的。而国外的习惯正好相反(老师说的,还没接过国外的邮件,谁能寄个瞅瞅  )。

从顶部開始:

1
2
3
4
5
select
sys_connect_by_path (title,
'/')
from
tb_menu
where
id =
50
start
with parent is
null
connect
by parent = prior id;

从当前节点開始:

1
2
3
4
select
sys_connect_by_path (title,
'/')
from
tb_menu
start
with id =
50
connect
by prior parent = id;

在这里我又不得不放个牢骚了。oracle仅仅提供了一个sys_connect_by_path函数。却忘了字符串的连接的顺序。

在上面的样例中,第一个sql是从根节点開始遍历,而第二个sql是直接找到当前节点,从效率上来说已经是千差万别。更关键的是第一个sql仅仅能选择一个节点。而第二个sql却是遍历出了一颗树来。再次ps一下。

sys_connect_by_path函数就是从start with開始的地方開始遍历,并记下其遍历到的节点。start with開始的地方被视为根节点,将遍历到的路径依据函数中的分隔符,组成一个新的字符串,这个功能还是非常强大的。

11)、列出当前节点的根节点。

在前面说过。根节点就是start with開始的地方。

1
2
3
4
select
connect_by_root title, tb_menu.*
from
tb_menu
start
with id =
50
connect
by prior parent = id;

connect_by_root函数用来列的前面,记录的是当前节点的根节点的内容。

12)、列出当前节点是否为叶子。

这个比較常见。尤其在动态文件夹中,在查出的内容是否还有下级节点时,这个函数是非常适用的。

1
2
3
4
select
connect_by_isleaf, tb_menu.*
from
tb_menu
start
with parent is
null
connect
by parent = prior id;

connect_by_isleaf函数用来推断当前节点是否包括下级节点。假设包括的话,说明不是叶子节点,这里返回0;反之。假设不包括下级节点,这里返回1。

至此,oracle树型查询基本上讲完了。以上的样例中的数据是使用到做过的项目中的数据,由于里面的内容可能不好理解,所以就所实用一些新的样例来进行阐述。

以上所有sql都在本机上測试通过,也都能实现对应特征,不过,这并不保证该最佳的解决方案,以解决上述问题(作为第一8文章写存储过程显著更好).

版权声明:本文博客原创文章,博客,未经同意,不得转载。

IT该忍者神龟Oracle 树操作(select…start with…connect by…prior)的更多相关文章

  1. [转]Oracle 树操作(select…start with…connect by…prior)

    转自http://www.cnblogs.com/linjiqin/archive/2013/06/24/3152674.html Oracle 树操作(select-start with-conne ...

  2. Oracle 树操作(select…start with…connect by…prior)

    摘自:http://www.cnblogs.com/linjiqin/archive/2013/06/24/3152674.html oracle树查询的最重要的就是select…start with ...

  3. oracle树操作(select start with connect by prior)

    oracle中的递归查询可以使用:select .. start with .. connect by .. prior 下面将会讲述oracle中树形查询的常用方式,只涉及到一张表. 一. 建表语句 ...

  4. Oracle学习之Oracle 树操作(select…start with…connect by…prior)

    转自:http://www.cnblogs.com/linjiqin/archive/2013/06/24/3152674.html oracle树查询的最重要的就是select…start with ...

  5. (转载)Oracle 树操作(select…start with…connect by…prior)

    转载地址:https://www.cnblogs.com/linjiqin/p/3152674.html 备注:如有侵权,请立即联系删除. oracle树查询的最重要的就是select…start w ...

  6. [转]Oracle 树操作(select…start with…connect by…prior)

    原文地址:https://www.cnblogs.com/colder/p/4838574.html oracle树查询的最重要的就是select…start with…connect by…prio ...

  7. Oracle 树操作(select…start with…connect by…prior)---转

    原文地址:http://www.cnblogs.com/linjiqin/p/3152674.html -----------

  8. Oracle树查询,start with connect by prior 递归查询用法(转载)

    本人觉得这个写的真不错,实用性强,就转载过来了 这个子句主要是用于B树结构类型的数据递归查询,给出B树结构类型中的任意一个结点,遍历其最终父结点或者子结点. 先看原始数据: 1 create tabl ...

  9. Oracle 树操作

    Oracle 树操作(select…start with…connect by…prior) oracle树查询的最重要的就是select…start with…connect by…prior语法了 ...

随机推荐

  1. swift学习第十五天:闭包

    闭包 闭包的介绍 闭包和OC中的block非常相似 OC中的block是匿名的函数 Swift中的闭包是一个特殊的函数 block和闭包都经常用于回调 注意:闭包和block一样,第一次使用时可能不习 ...

  2. Swift 语言概览 -自己在Xcode6 动手写2-tableView

    import UIKit class ViewController: UIViewController ,UITableViewDelegate, UITableViewDataSource { va ...

  3. 【63.73%】【codeforces 560A】Currency System in Geraldion

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  4. Jcaptca 图片

    http://www.oschina.net/code/snippet_89964_6009 http://blog.csdn.net/tender001/article/details/846969 ...

  5. css3-4 css3边框样式

    css3-4 css3边框样式 一.总结 一句话总结: 二.css3边框样式 1.相关知识 边框属性:1.border-width2.border-style3.border-color 边框方位:1 ...

  6. STL map 按key值和按value值排序

    map是用来存放<key, value>键值对的数据结构,能够非常方便高速的依据key查到对应的value. 假如存储水果和其单位价格.我们用map来进行存储就是个不错的选择. 我们这样定 ...

  7. 基于 Android NDK 的学习之旅-----JNI LOG 打印

    程序都是调出来的. 下面我介绍下JNI层的log打印方法的使用,类似与Android sdk提供的log 1.Android 应用层 MainActivity.java 主要功能代码 a)       ...

  8. 《写给大忙人看的Java SE 8》——Java8新特性总结

    阅读目录 接口中的默认方法和静态方法 函数式接口和Lambda表达式 Stream API 新的日期和时间 API 杂项改进 参考资料 回到顶部 接口中的默认方法和静态方法 先考虑一个问题,如何向Ja ...

  9. 物联网学生科协第三届H-star现场编程比赛

    问题 A: 剪纸片 时间限制: 1 Sec 内存限制: 128 MB 题目描写叙述 这是一道简单的题目,假如你身边有一张纸.一把剪刀.在H-star的比赛现场,你会这么做: 1. 将这张纸剪成两片(平 ...

  10. sqlplus中登陆账户用@加上数据库sid

    sqlplus连接数据库时除了先输入用户名再输入密码的方式还有一种直接输入方式,而且使用@sid区分数据库,在有多个数据库时可方便区分 connect sys/sysdb@oraclesid as s ...