oracle 递归查询

SELECT  *   FROM    ORG_table
   CONNECT BY PRIOR  org_code=parent_code  START WITH org_code='000' 
   





写代码时碰到要弄清楚Oracle的role之间的传递关系,就是有role A的话,可以通过grant A to B,把A赋予给B,又通过grant B to C .那我想知道所有role中,有哪些role具有A的权限.

上网一查发现有个递归查询,不过都讲的不是太详细,而那Oracle整的那用法实在太怪异了,跟我们平时用的SQL差的太远,所以琢磨了好一阵子脑子才转过弯呢.

树形结构

可能一看到递归查询这样太专业的名词大家就迷糊了.实际上可以看成有一个树形结构,然后我们要怎么把所有树的所有结点查找出来.学数据结构的时候我们知道要遍历一个树结构有啥前序遍历,中序遍历,后序遍历.反正挺麻烦的.不像遍历个数组那么容易的.那实际上在Oracle的一个表中也可以保存树形结构信息.你要查询所有的树节点,自己整个函数或存储过程去整肯定是超级麻烦的.Oracle提供了一个简单的机制帮助你.要用到start with ...connect by等关键字.先来假定有下面这样一个简单的树形结构存储在表中.

create table Tree(son char(10), father char(10)); 然后插入些信息变成这样的表

 

SON            FATHTER

孙子SB         儿子

孙子NB         儿子

儿子             爸爸

爸爸             爷爷

 

很显然这是一个简单的树形结构

                           孙子SB

                             ^

                             |

爷爷 --> 爸爸 --> 儿子 -->孙子NB

                          

 

递归查询

假如要查询出以爷爷为根的树的所有节点值咋整呢 ?如果数据少多来几个where嵌套就行.但要是树层次有几百那会搞死人了啊.于是我们就用Oracle提供的递归查询.先看下SQL的写法然后再讲解

SELECT  son FROM tree

START WITH father = '爷爷'

CONNECT BY PRIOR son = father;

返回的结果为 爸爸 儿子 孙子NB 孙子SB

代码看起来很短,但是极为怪异,脑子半天都不容易转过弯呢.实际上我们不把这个SQL语句跟一般的SQL类比,而把它当作给一些函数指定一些参数的赋值语句才更容易理解.

那怎么来理解上面的SQL呢?

首先把SELECT son FROM tree还是看成一般sql一样,就是要查找son这一列的信息.而把以START WITH开头的后面所有东东看成一个where限制条件.其中START WITH 是指定

树的根,这里指定的根是 '爷爷',实际上你还可以指定多个根的,比如 father in ('爷爷', '爸爸') .

而CONNECT BY PRIOR son = father相当于表明在递归的过程中,查找到的树中其它节点接着又作为根结点.然后继续递归. 反正看这sql语句前先想下树形结构,然后想下一般编程语言中的递归函数.再来看时就容易理解点.实际上我觉得Oracle这样设计不太好.如果用户只是简单的指定的一个根节点然后知道树中其他节点信息.那么就直接用START WITH指定根就行了.CONNECT BY PRIOR显得有点多余,可以不用用户去指定.当作一个默认值,只有需要其他一些更复杂的操作时才让用户明确指定.这样就不容易误导人了.

为了便于理解可以可以CONNECT BY那一行当作多余的,只记住要查询的列名放前面,根列名放等号后面就行.这样只要指定树的根结点就比较好理解了.

 

start with ,connect by prior其他变形

上面讲的用START WITH 指定树的根,然后用CONNECT BY指定递归条件.是最简单的也是最常用的形式.但实际上还有些变形.

1.START WITH 可以省略

比如

SELECT son FROM tree

CONNECT BY PRIOR son = father;

此时不指定树的根的话,就默认把Tree整个表中的数据从头到尾遍历一次,每一个数据做一次根,然后遍历树中其他节点信息.

在这个例子中,上面的SQL等价于

SELECT son FROM tree

START WITH father IN (爷爷,爸爸,儿子,孙子NB,孙子SB)

CONNECT BY PRIOR son = father;

那查询到的结果如下,有很多重复信息的

爸爸,儿子,孙子NB,孙子SB    儿子,孙子NB,孙子SB    孙子NB,孙子SB

 

2.START WITH 与CONNECT BY PRIOR位置可互换

 

SELECT son FROM tree

CONNECT BY PRIOR son = father

START WITH father = '爷爷';

这语句与最开头那个是等价的

 

3.nocycle关键字

我们知道标准的树结构中是不会有环的,但表中的树形结构不是标准的,有可能导致环的出现

比如

             ---------孙子SB

             |              ^

             |              |

爷爷 --> 爸爸 --> 儿子 -->孙子NB

哎在这里想用线条整个箭头出来真他妈麻烦啊.我又有点懒不想用其他画图工具啥的啊.反正假设儿子的儿子是孙子SB ,而孙子SB的儿子是爸爸.这样就形成一个环了.

当然在Oracle中的role是禁止出现循环的.比如你grant A to B ,grant B to C .再来个grant C to A会出错的.

假如有个上面的环,在再使用开始的递归查询语言会出错.得用nocycle关键字指定忽略环.

SELECT son FROM tree

START WITH father = '爷爷'

CONNECT BY NOCYCLE PRIOR son = father;

此时结果是

爸爸 儿子 孙子NB

你会注意到由于忽略了环,所以孙子SB的信息也被忽略掉了.

 

4. connect by prior 后面条件顺序的改变 (???)

SELECT son FROM tree

START WITH father = '爷爷'

CONNECT BY PRIOR son = father;

这是开头的写法,但实际上也可以写成father = son倒过来写.有人说没倒过来是从上到下,从根往下.如果倒过来则是从下到上.哎不过我测试了下发现不是那么回事.结果也有点乱七八糟的.没想明白是啥规律.反正还有待研究啊

 

5.还可以加where条件

我上面说了可以把start with ,connect 假装看成where 条件一样.所以在这个sql语句其他地方还可以加其他where 语句,可以看成与递归查询无关.只是对整个结果起过滤作用

比如

SELECT son FROM tree  WHERE son = '孙子SB'

START WITH father = '爷爷'

CONNECT BY PRIOR son = father;

 

当然你不能在最后部分加where,不能在connect by最后面再加.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Oracle递归查询是一种查询技术,用于查询表中的层次结构数据,例如查询某个节点的父节点或子节点。在Oracle中,可以使用start with connect by prior或with递归查询来实现递归查询。 使用start with connect by prior递归查询,可以查询所有子节点、所有父节点、指定节点的根节点以及指定节点的递归路径。这种查询方法通过在查询条件中使用prior关键字来指定当前数据和下一条数据之间的关系。例如,使用START WITH子句指定起始节点,然后使用CONNECT BY子句指定节点之间的关系,可以实现向上或向下递归查询。 使用with递归查询,可以通过递归调用查询多层结构的子节点或父节点。这种查询方法使用WITH子句定义递归查询的初始条件和递归关系,并使用递归子查询来实现递归查询。 需要注意的是,递归查询可能会导致查询时间特别长,特别是在数据量特别大的情况下。因此,在进行递归查询时,需要谨慎考虑查询的效率和性能。 综上所述,Oracle提供了递归查询语句来实现对层次结构数据的查询,包括start with connect by prior和with递归查询。这些查询方法可以帮助我们方便地查询父节点和子节点的关系。 #### 引用[.reference_title] - *1* [Oracle递归查询](https://blog.csdn.net/Michael_lcf/article/details/124433725)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Oracle递归查询树形数据](https://blog.csdn.net/weixin_40017062/article/details/127653569)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [ORACLE递归查询](https://blog.csdn.net/m0_46636892/article/details/122984132)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值