Oracle树形表和递归查询

地址:https://blog.csdn.net/hellowordapi/article/details/75763432

在平常的业务系统开发中,我们经常需要设计数据层次关系,如在经典的user-role-permission权限设计中, 需要对权限表的数据设计成一种层次依赖关系,如最顶层的为系统管理,系统管理的下一层为角色 管理,角色管理的下一层又为角色的CRUD操作, 那么这种表就可以抽象成为数据结构里面的B树. 如下表 :

CREATE TABLE "U_PERMISSION"
( "ID" NUMBER(20,0),
"URL" VARCHAR2(256 BYTE),
"NAME" VARCHAR2(50 BYTE),
"PARENT" NUMBER(20,0)
)

在上表中 id表示当前树的节点。url, name表示可访问的url路径,name表示url描述。 parent表示当前节点的父节点,如果当前节点是跟节点则parent用0表示(别用NULL违反了数据库约束)。 那么上面的表就可以抽象成如下图.

接着我们插入测试数据 :

Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (1,‘*‘,‘系统管理‘,0);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (2,‘*‘,‘权限管理‘,1);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (20,‘/role/allocation‘,‘角色分配‘,23);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (4,‘/permission/index‘,‘权限列表‘,2);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (6,‘/permission/addPermission.shtml‘,‘权限添加‘,2);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (7,‘/permission/deletePermissionById‘,‘权限删除‘,2);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (8,‘/member/list.shtml‘,‘用户列表‘,22);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (9,‘/member/online.shtml‘,‘在线用户‘,22);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (10,‘/member/changeSessionStatus‘,‘用户Session踢出‘,22);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (11,‘/member/forbidUserById‘,‘用户激活or禁止‘,22);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (12,‘/member/deleteUserById‘,‘用户删除‘,22);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (13,‘/permission/addPermission2Role‘,‘权限分配‘,2);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (14,‘/role/clearRoleByUserIds‘,‘用户角色分配清空‘,23);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (15,‘/role/addRole2User‘,‘角色分配保存‘,23);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (16,‘/role/deleteRoleById.shtml‘,‘角色列表删除‘,23);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (17,‘/role/addRole‘,‘角色列表添加‘,23);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (18,‘role/index‘,‘角色列表‘,23);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (19,‘/permission/allocation‘,‘权限分配2‘,2);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (22,‘*‘,‘用户管理‘,1);
Insert into U_PERMISSION (ID,URL,NAME,PARENT) values (23,‘*‘,‘角色管理‘,1);
既然都已经创建树形表了那么肯定要对树进行操作。就像我们在大学的数据结构一书中所做的那样对树进行递归遍历(前序,中序,后序. 忘记了的面壁....) 在oralce中通过

start with....connect by...prior语法,依托该语法我们就可以对上面树形表进行递归遍历。

示例:

给出一个节点的值,求出的他父节点和祖宗节点:

start with子句: 递归的条件,需要注意的是如果with后面的值是子节点那么求出的就是他的父节点和祖宗节点,如果是父节点那么求出的就是他的子节点和子孙节点,

如果不懂可以把上面start with 后面的条件改成 p.parent=0,就可以理解了。

connect by子句:连接条件。 关键词prior,prior跟它右边的父节点放在一起(prior p.parent)表示往父节点方向遍历, 反之,如果 prior跟子节点放在一起(prior p.id)表示往

叶子方向遍历。 这里需要注意的 =p.id 放在prior关键词的前面或者后面都没什么关系,也就是上面可以这样写 p.id= prior p.paren。重要的是prior旁边放的

是什么。

level伪列: 递归的层次表示, 用来进行输出缩进。可以看到递归层次,看起来很直观。 需要注意的是Level 也可以放在Group by后面,也可以放在select 后面.

下面我来讲一下递归语法的一些用法和一些需要注意的地方:

有趣的是我们可以不使用start with 子句,它不像程序语言一样不写就会造成死循环(插一句嘴,曾经有一人和我聊天提到SQL不是编程语言,因为他不满足图灵完全。因为他并不能造成死循环,其实通过递归语句就可以,这个坑我会在后面演示,在本文中并不讨论SQL是不是编程语言)。它只会把会整个表的数据遍历一遍,每一个数据做一次

根节点,然后遍历树中的其他节点。

他等价于如下SQL:

select p.ID,p.NAME,p.PARENT, level from u_permission p start with p.PARENT in( ‘祖宗节点‘,‘父节点‘,‘子节点‘,‘孙子节点‘ ) connect by p.id= prior p.parent;

2. start with 和connect by prior的位置可以互换:

select p.ID,p.NAME,p.PARENT, level from u_permission p start with p.PARENT=1 connect by p.id= prior p.parent;
select p.ID,p.NAME,p.PARENT, level from u_permission p connect by p.id= prior p.parent start with p.PARENT=1;
上面二条的SQL意思是一样的。

3.我们可以把start with看成是一个where,所以这也意味着我们可以在我们可以在除了最后部分以外的地方加where条件, 如:

select p.ID,p.NAME,p.PARENT, level from u_permission p where p.id=xxx start with p.PARENT=1 connect by p.id= prior p.parent;
但是这里隐藏着一个坑, where条件的作用域不是在递归查询中的,而是在递归查询完后的。 所以如果理解不当他可以造成死循环(虽然会检测出来报ORA错误)也就是a的父节点是b, b的父节点是a。

看一个实用的列子:

如果有一个需求如根据user表的id查出他的权限树,这个时候我们就需要start with后面加子查询。

我们可以看到根据子查询返回的根节点查出所有子节点的父亲节点和祖宗节点。

下面是一些跟递归语法相关的函数(来自Oracle SQL高级编程):

SYS_CINNECT_BY_PATH函数:

这个函数是用来返回组成层级的直到当前行的值, 我一图胜千言把。

简单来说就是用来做拼接的,至于想拼接什么看各位看官的心情咯。

CONNECT_BY_ISLEAF伪列:

这个伪列呢,说直白一点跟level的作用差不多,只不过他是用来在递归查询中显示叶子节点而非递归深度。 留给看官实验,不上传实验图片了。

CONNECT_BY_ISCYCLE伪列 和NOCYCLE:

这个函数用于检测树中是不是出现了环,传统的数据结构树是不会出现环的,只有在图中才会出现环。 而在数据库中我说过,他不是正儿八经

的B树,他可能会出现一些不合人伦的事情,比如就像上面所说的死循环, a是b的父亲,b又叫a儿子这样尴尬的事情,Oracle会直接报出ORA-1436的

错误,告诉你这样不太好。 所以我们可以用nocycle函数解决这个问题,也就是让b当儿子。使得SQL符合逻辑。

select connect_by_isleaf ,p.ID,p.NAME,p.PARENT, CONNECT_BY_ISCYCLE , level from u_permission p start with p.PARENT=1 connect by nocycle p.id= prior p.parent;

connect_by_isleaf  函数:

在递归中检测当前节点是否包含下级节点,也就是说是不是叶子节点,是返回0, 不是返回1,  在动态的目录中有用。

select connect_by_isleaf ,p.ID,p.NAME,p.PARENT, level from u_permission p start with p.PARENT is null connect by p.id= prior p.parent;

原文地址:https://www.cnblogs.com/mark5/p/12303021.html

时间: 2024-07-31 01:12:39

Oracle树形表和递归查询的相关文章

SQL Server 树形表非循环递归查询

很多人可能想要查询整个树形表关联的内容都会通过循环递归来查...事实上在微软在SQL2005或以上版本就能用别的语法进行查询,下面是示例. --通过子节点查询父节点WITH  TREE AS(      SELECT * FROM Areas      WHERE id = 6  -- 要查询的子 id     UNION ALL      SELECT Areas.* FROM Areas, TREE      WHERE TREE.PId = Areas.Id )  SELECT Area

集成代码生成器 单表 多表 树形表 一对多 springmvc spring mybatis SSM 后台框架

获取[下载地址]   QQ: 313596790   [免费支持更新] 三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体 [新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单; QQ:313596790 freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块 B 集成阿里巴巴数据库连

【集成代码生成器】 单表 多表 树形表 一对多

获取[下载地址]     [免费支持更新]三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体[新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单; freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块B 集成阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.

【集成代码生成器】 单表 多表 树形表 一对多 springmvc spring mybatis SSM 后台框架

获取[下载地址]   [免费支持更新]三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体[新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单; freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块B 集成阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.Dr

单表 多表 树形表 一对多 springmvc

获取[下载地址]   [免费支持更新]三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体[新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单; freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块B 集成阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.Dr

集成代码生成器 单表 多表 树形表 一对多 springmvc spring mybatis S

获取[下载地址]   QQ: 313596790   [免费支持更新]三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体[新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单; QQ:313596790freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块B 集成阿里巴巴数据库连接池dr

oracle热点表online rename

对于在线的繁忙业务表的任何操作都可能带来意想不到的风险.一张业务表,对partition key进行升位,其步骤是: rename原表 新建临时表 交换分区到临时表 升位临时表的字段的长度 交换临时表到第二张临时表 rename第二种临时表为业务表 整个的操作过程如果顺利,预计在10s左右,如果放在文件中,速度会很快.下面模拟繁忙表进行测试: #!/bin/sh . /home/oracle/.bash_profile sqlplus -S /nolog<<EOF conn test/test

oracle 11G表压缩

最近一套生产库表空间一直告警在90%以上,但的磁盘硬件资源又不足,整个库已经达到26T.库里存储了近4年的数据,与业务沟通说历史数据基本上不会做操作,但是又不能归档,所以想到了压缩表来节省表空间. 随着数据库的增长,我们可以考虑使用oracle的表压缩技术.表压缩可以节省磁盘空间.减少data buffer cache的内存使用量.并可以显著的提升读取和查询的速度.当使用压缩时,在数据导入和DML操作时,将导致更多的CPU开销,然而,由于启用压缩而减少的I/O需求将抵消CPU的开销而产生的成本.

Oracle Temp 表空间切换

一.TEMP表空间作用 临时表空间主要用途是在数据库进行排序运算.管理索引.访问视图等操作时提供临时的运算空间,当运算完成之后系统会自动清理.当 oracle 里需要用到 sort 的时候, PGA 中 sort_area_size 大小不够时,将会把数据放入临时表空间里进行排序,同时如果有异常情况的话,也会被放入临时表空间 , 正常来说,在完成 Select 语句.create index 等一些使用 TEMP 表空间的排序操作后, Oracle 是会自动释放掉临时段的.注意这里的释放,仅仅是