使用connect by进行级联查询

2023-11-18

connect by可以用于级联查询,常用于对具有树状结构的记录查询某一节点的所有子孙节点或所有祖辈节点。

 

       来看一个示例,现假设我们拥有一个菜单表t_menu,其中只有三个字段:id、name和parent_id。它们是具有父子关系的,最顶级的菜单对应的parent_id为0。现假设我们拥有如下记录:

id

name

parent_id

1

菜单01

0

2

菜单02

0

3

菜单03

0

4

菜单0101

1

5

菜单0102

1

6

菜单0103

1

7

菜单010101

4

8

菜单010201

5

9

菜单010301

6

10

菜单0201

2

11

菜单0202

2

12

菜单020101

10

13

菜单020102

10

14

菜单020103

10

15

菜单0301

3

16

菜单0302

3

17

菜单030201

16

18

菜单030202

16

19

菜单030203

16

 

       如果这个时候我们需要查询“菜单01”以及其下所有的子孙菜单应该怎么办呢?如果使用connect by的话这将会非常简单,使用如下SQL语句就可以达到对应的效果。

Sql代码   收藏代码
  1. select * from t_menu connect by parent_id=prior id start with id=1;  

  

       connect by是需要跟start with一起使用的。connect by后跟的是连接条件,在connect by后接的条件通常都需要使用关键字“prior”,可以简单的把它理解为上一级,所以上述例子中“connect by parent_id=prior id”就表示连接条件为parent_id等于上级的id,查找到下一级记录后又会找parent_id等于下一级记录的id的记录,而prior对应的最顶层的记录就是通过start with来确定的,start with后接对应的筛选条件,表示最顶层的记录是哪些,最顶层的记录可以有多个,比如我想查找“菜单01”下的子孙菜单,但是不包括“菜单01”本身,那么我就可以使用如下的SQL语句进行查找,此时“start with parent_id=1”对应的记录就会有多条。

Sql代码   收藏代码
  1. select * from t_menu connect by parent_id=prior id start with parent_id=1;  

 

       对应的结果为:

id

name

parent_id

4

菜单0101

1

5

菜单0102

1

6

菜单0103

1

7

菜单010101

4

8

菜单010201

5

9

菜单010301

6

 

       此外,如果我们想查找“菜单010101”对应的祖辈菜单也非常简单,如下SQL就可以实现该功能,即从“菜单010101”的父菜单(对应id为4)开始查找。

Sql代码   收藏代码
  1. select * from t_menu connect by id=prior parent_id start with id=4;  

 

       对应的结果为:

id

name

parent_id

1

菜单01

0

4

菜单0101

1

 

level

       使用connect by时我们可以使用内置的类似于rownum的一个叫level的伪列,该列表示当前记录相对于start with记录的一个层级,start with记录的level为1。如上面的两条SQL语句,如果加上level的话对应的结果将是这样的。

Sql代码   收藏代码
  1. select level,t.* from t_menu t connect by parent_id=prior id start with parent_id=1;  

 

       对应的结果为:

level

id

name

parent_id

1

4

菜单0101

1

1

5

菜单0102

1

1

6

菜单0103

1

2

7

菜单010101

4

2

8

菜单010201

5

2

9

菜单010301

6

 

Sql代码   收藏代码
  1. select level,t.* from t_menu t connect by id=prior parent_id start with id=4;  

 

       对应的结果为:

level

id

name

parent_id

2

1

菜单01

0

1

4

菜单0101

1

 

       有了level后,我们就可以对查询的level做一个限制,比如只查从最顶层开始向下两级的菜单。

Sql代码   收藏代码
  1. select level,t.* from t_menu t where level<3 connect by prior id= parent_id start with parent_id=0;  

  

       从上述SQL我们可以看到where条件是直接跟在from之后的,使用connect by时我们的where条件不是在connect by之前对数据进行过滤的,而是在connect by之后才对所有的数据进行过滤的,这一点跟使用分组语句group by时是不一样的,group by是先通过where对需要分组的数据进行过滤后再通过group by来分组的。

 

nocycle和connect_by_iscycle

       如果我们的记录中存在循环的父子关系,则使用connect by进行查询时会抛出异常,如A->B、B->C、C->A这样的记录。解决办法是在connect by语句后加上“nocycle”,表示不循环查询,如:

Sql代码   收藏代码
  1. select * from t_menu connect by nocycle prior id=parent_id start with parent_id=0;  

  

       使用nocycle后对于A->B、B->C、C->A这样的记录会通过查询B,然后通过B查询C,再通过C查询A时发现已经循环了,就不再查询了,即在C这条记录这里循环了。在对存在循环记录的查询中我们也可以通过“connect_by_iscycle”找到是哪一条记录循环了,“connect_by_iscycle”也是一个伪列,其必须和nocycle一起使用。伪列“connect_by_iscycle”对应的值有0和1,如果某一条记录的connect_by_iscycle对应的值为1则表示从该条记录这里开始循环了。如下是一个使用connect_by_iscycle的示例。

Sql代码   收藏代码
  1. select connect_by_iscycle,t.* from t_menu t connect by nocycle prior id=parent_id start with parent_id=0;  

  

connect_by_isleaf

       connect_by_isleaf也是一个伪列,其表示对应的记录是否是一个叶子节点,即在进行connect by时不能通过该记录找到下一条记录。其对应的值有0和1,0表示非叶子节点,1表示是叶子节点。如我只想找出是叶子节点的菜单时对应的SQL可以这样写:

Sql代码   收藏代码
  1. select connect_by_isleaf,t.* from t_menu t where connect_by_isleaf=1 connect by prior id=parent_id start with parent_id=0;  

  

connect_by_root

       connect_by_root表示根节点,即某一条记录所对应的最顶级的记录,其用法跟prior类似,后面也需要跟一个字段名。如下面示例可以查询所有叶子节点菜单的最顶级菜单和上级菜单的名称。

Sql代码   收藏代码
  1. select connect_by_root name as root_name, prior name as prior_name,t.* from t_menu t where connect_by_isleaf=1 connect by prior id=parent_id start with parent_id=0;  

  

       对应上表的记录,在上述SQL中查询出来的结果应该如下所示:

root_name

prior_name

id

name

parent_id

菜单01

菜单0101

7

菜单010101

4

菜单01

菜单0102

8

菜单010201

5

菜单01

菜单0103

9

菜单010301

6

菜单02

菜单02

11

菜单0202

2

菜单02

菜单0201

12

菜单020101

10

菜单02

菜单0201

13

菜单020102

10

菜单02

菜单0201

14

菜单020103

10

菜单03

菜单03

15

菜单0301

3

菜单03

菜单0302

17

菜单030201

16

菜单03

菜单0302

18

菜单030202

16

菜单03

菜单0302

19

菜单030203

16

 

sys_connect_by_path

       sys_connect_by_path(column,delimiter)可以用来展示以指定column和分隔符delimiter表示从根节点到当前节点的路径。以下SQL用来查询id为2的菜单下叶子节点的信息,包括以字段name和分隔符“>”表示的其对应的根节点的路径。

Sql代码   收藏代码
  1. select sys_connect_by_path(name'>'as connect_path,t.* from t_menu t where connect_by_isleaf=1 connect by prior id=parent_id start with id=2;  

 

       对应结果如下所示:

connect_path

id

name

parent_id

>菜单02>菜单0202

11

菜单0202

2

>菜单02>菜单0202>菜单020101

12

菜单020101

10

>菜单02>菜单0202>菜单020102

13

菜单020102

10

>菜单02>菜单0202>菜单020103

14

菜单020103

10

 

排序order

       可以使用order by对connect by之后的结果进行排序,此时order by需放在最末端,而不像where筛选那样直接定义在from之后。如需对connect by之后的结果按id进行排序,则可以使用如下SQL语句:

Sql代码   收藏代码
  1. select t.* from t_menu t connect by parent_id=prior id start with parent_id=0 order by id;  

  

       除了传统的针对查询结果的排序外,connect by语句还支持对同一父节点下的子节点进行排序,这是通过order siblings by来定义的。如我们需要查询id为2的菜单下的所有子孙菜单,然后对具有同一父节点的菜单按id进行倒序排列,则我们的SQL语句可以如下定义:

Sql代码   收藏代码
  1. select t.* from t_menu t connect by parent_id=prior id start with id=2 order siblings by id desc;  

  

       对应的结果会是这样子:

id

name

parent_id

2

菜单02

0

11

菜单0202

2

10

菜单0201

2

14

菜单020103

10

13

菜单020102

10

12

菜单020101

10

 

       如上表所示,我们可以看到“菜单0201”和“菜单0202”具有相同的父节点“菜单02”,它们按照id进行倒序排列,所有“菜单0202”在“菜单0201”之前,同样“菜单020101”、“菜单020102”和“菜单020103”具有相同的父节点“菜单0201”,所以它们也是按照id的倒序排列。

   

一次针对connect by的查询优化

       有这么一个需求:表A表示分类,表B表示任务模板,A与B是一对多的关系,每一个任务模板都属于一个特定的分类,在表B中用字段a表示所属的分类。分类存在父子关系,子分类的parent_id对应父分类的id。现假设需要统计id为1的分类及其子分类下存在的任务模板数量。对应SQL如下:

Sql代码   收藏代码
  1. select count(1) from B b,(select id from A connect by prior id=parent_id start with id=1) a where a.id=b.a;  

 

       现假设拥有另外一个表C,其表示任务实例,一个任务模板B可以拥有n个任务实例B,即B跟C之间是一对多的关系。任务实例C通过字段b关联任务模板B,另外任务实例C拥有一个字段status表示任务实例的具体状态。现假设需要统计id为1的分类及其子分类下各状态的任务实例数量。对应SQL如下:

Sql代码   收藏代码
  1. select c.status,count(1) from B b,(select id from A connect by prior id=parent_id start with id=1) a, C c where a.id=b.a and b.id=c.b group by c.status;  

 

       在A表数据量1000,B表数据量20000,C表数据量5000,id为1的分类下属的子孙分类数量为100的情况下第一条SQL的查询速度可以在0.1秒左右完成,而第二条SQL需要将近10秒才能完成。把查询id为1的分类下子孙分类的id的SQL语句“selectidfrom A connectbypriorid=parent_id startwithid=1”单独查询的速度也可以在0.1秒内完成。通常对于这种数量级别的三表查询都是可以在0.1秒内完成的,为此心想第二条SQL应该是受了子查询中connect by的影响。后来决定把分类的子查询直接作为B的in条件进行查询,如下所示:

Sql代码   收藏代码
  1. select c.status,count(1) from B b, C c where b.a in(select id from A connect by prior id=parent_id start with id=1) and b.id=c.b group by c.status;  

 

       其查询效果是一样的,心想应该还是connect by影响到了,既然单独使用connect by查询id为1的分类的子孙分类的id只需要不到0.1秒,那何不在程序里面先将id为1的分类的子孙分类id查询出来,再作为B、C联合查询的in条件,如:

Sql代码   收藏代码
  1. select c.status,count(1) from B b, C c where b.a in(...) and b.id=c.b group by c.status;  

 

       结果查询结果也可以在0.1秒内完成。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用connect by进行级联查询 的相关文章

  • Oracle 11g XE - 突然出现 ORA-01034: ORACLE 不可用且 ORA-27101: 共享内存领域不存在

    Windows 7 32 位笔记本电脑 Oracle 11g XE 的本地实例 首先 在提出问题之前 我自己一直在研究这个问题 虽然有很多参考 建议 但我无法自己解决这个问题 突然 当我尝试登录本地 Oracle 11g XE 爱好者实例时
  • 插入期间违反唯一约束:为什么? (甲骨文)

    我正在尝试在表中创建一个新行 表上有两个约束 一个是关键字段 DB ID 另一个是将值限制为字段 ENV 中的几个之一 当我进行插入时 我没有将关键字段作为我尝试插入的字段之一 但我收到此错误 unique constraint N390
  • 如何查看授予其他用户的架构哪些权限

    考虑这种情况 在数据库中 我有两个用户 A 和 B 及其相应的架构 我想知道 如何获取信息 架构 B 中的用户 A 有哪些权限 考虑这种情况 我们有两个用户及其关联的场景 我们有用户A和用户B 在A中 假设我们有TB1 TB2 在B中 假设
  • 关于pl/sql存储程序文本的问题

    我使用 TOAD 进行 PL SQL 开发 在 TOAD 中 当我输入过程名称并按 f4 时 我可以看到该过程的源代码 我认为 TOAD 从 v sqltext 视图获取源代码 为了证实我的想法 我写了一个查询 select from v
  • Oracle ODP.net 托管驱动程序与非托管驱动程序

    托管和非托管 Oracle ODP Net 驱动程序之间是否存在任何性能基准 即 除了架构 部署简单性之外 迁移到托管驱动程序是否有任何优势 我想分享一些结果 我认为与部署的简便性相比 性能上的小缺陷是值得的 Note seg意味着秒 对于
  • ActiveRecord oracle_enhanced 适配器无法加载 ruby​​-oci8 库

    rails g scaffold失败 但 oci 脚本和 irb 数据查询有效 无法弄清楚出了什么问题 rails g scaffold table field1 integer field2 string invoke active re
  • 数百个别名/同义词与数据库表的完全限定名称

    考虑到多个模式中的数百个数据库表 在创建存储过程和视图时 您是否建议使用别名 同义词或完全限定名称 给定一些 schema table 像这样 Orders OrderHeader Production LineThroughput Sal
  • 如何从 TOAD for Oracle 执行函数并将结果绑定到数据网格

    我有一个函数 它将 pl sql 对象的 VARRAY 作为其参数之一 如何执行此存储过程并将其返回的结果集绑定到 TOAD for Oracle 中的数据网格 经过一番搜索后 我找到了自己问题的答案 假设您的 varray 类型称为 va
  • 递归 SQL 给出 ORA-01790

    使用 Oracle 11g 第 2 版 以下查询给出 ORA 01790 表达式必须与相应表达式具有相同的数据类型 with intervals time interval AS select trunc systimestamp from
  • Oracle:使用SQL或PL/SQL查找动态SQL中的错误位置

    如何在 PL SQL 或 SQL 中找到动态 SQL 语句中的错误位置 从 SQL Plus 中 我看到了错误的位置 例如 无效的 SQL DML 语句 SYS orcl gt SELECT 2 X 3 FROM 4 TABLEX 5 TA
  • REgex从oracle中的字符串中获取数字

    我有以下格式的字符串 阿克拉姆 88 jamesstree 20140418 阿克兰 8 约翰街 20140418 阿克兰 888 johnstreet 20140418 现在我只想检索 88 8 和 888 值 我为此编写了以下查询 SU
  • APEX 安装失败,PLS-00201:必须声明标识符“SYS.DBMS_DB_VERSION”

    尝试在 Oracle XE 18c 数据库上安装 Oracle APEX 20 2 如下官方说明 https docs oracle com en database oracle application express 20 1 htmig
  • ORA-12154: TNS: 无法解析指定的连接标识符 (PLSQL Developer)

    我需要使用 PLSQL Developer 访问 oracle 数据库 当我尝试连接到数据库时出现以下错误 ORA 12154 TNS could not resolve the connect identifier specified 我
  • Oracle 语法 - 我们是否必须在新旧语法之间进行选择?

    我在一个由大约 8 名开发人员组成的团队中负责大约 1 000 000 行源代码的代码库 我们的代码基本上是一个使用 Oracle 数据库的应用程序 但代码随着时间的推移而不断发展 我们有大量九十年代中期的源代码 团队中就我们用于查询 Or
  • ora-04091 表正在发生变化-

    我正在使用一个函数来比较表 1 和表 2 中的所有列并返回 Y 或 N 在此基础上 我将更新我的表 1 但是当我运行合并语句时 它显示错误 ora 04091 Table1 正在发生变化 触发器 函数可能看不到它 我怎样才能解决这个问题 C
  • 如何在 Oracle PLSQL 中提交单个事务

    我需要编写一个 PL SQL 过程 在这个过程中 我需要在其自己的事务范围内调用另一个过程 并提交它 无论主事务是否失败或提交 换句话说 我需要类似的东西REQUIRES NEW交易传播 就像是 procedure mainProcedur
  • 在 Hibernate 中创建 UPDATE RETURNING 查询

    在 Oracle 中 我们可以创建一个更新查询 该查询将使用 RETURNING 子句返回更新的记录 Hibernate中有类似的功能吗 除了数据库生成的值之外 Hibernate 显然不需要返回更新的实例 因为对象传递给Session s
  • Windows 上良好的 Oracle 数据库开发和管理工具 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找像 sql server management studio 这样的 Oracle 数据库管理工具 我在互联网上搜索并在以下链接
  • 循环预定义值

    有没有办法在 oracle 中执行 for every 如下所示 begin for VAR in 1 2 5 loop dbms output put line The value VAR end loop end 我知道你可以这样做 b
  • 通知设置的数据库设计

    用户可以打开或关闭 他的通知设置 帐户 用于通知 例如 更改帐户资料信息 收到新消息等 通知可以通过电子邮件或手机 推送或短信 发送 用户可以只有 1 封电子邮件和多个手机设备 有什么方法可以改进以下数据库设计或者您会采取不同的方式吗 让我

随机推荐

  • 数据库 --- 约束

    一 什么是约束 常见的约束有那些 约束是在创建表的时候 可以给表的字段添加相应的约束 添加约束的目的是为了保证表中数据的合法性 有效性 完整性 常见的约束有 非空约束 not null 唯一约束 unique 主键约束 primary ke
  • Some Tips in Life

    How to Find Digital Books 1 http so baiduyun me 百度云搜索 2 http www zhaofile com 找文件 3 http www cnepub com 掌上书苑 4 http vdis
  • java定义时钟类clock_Java 编程题,定义一个时钟类(Clock)

    题目 Java 编程题 定义一个时钟类 Clock 要求如下 1 存储时钟的时hour 0 23 分minute 0 59 秒second 0 59 2 创建新对象时默认为0时0分0秒 3 设置时钟为指定的时间 4 使时钟前进1秒钟的功能i
  • jstat 命令

    NAME jstat Monitors Java Virtual Machine JVM statistics This command is experimental and unsupported SYNOPSIS jstat Opti
  • mongoDB数据库----简介

    目录 目录 一 NoSQL 1 关系型数据库遵循ACID规则 2 分布式系统 3 分布式计算的优点 4 分布式计算的缺点 5 什么是NoSQL 6 NoSQL 简史 7 NoSQL的优点 缺点 8 NoSQL 数据库分类 二 MongoDB
  • 你知道ChatGPT有哪些商业价值吗?不知道,那没意思

    这段时间 热度zui大的是什么 答案是 ChatGPT 去年11月底上线 当时仅在AI和科技圈内小火了一把 没想到在今年春节后 火爆出圈 ChatGPT的爆火 对商家和品牌方 还有投资创业者来说 是个机遇 普通人虽然很难参与到这些高科技的投
  • Python 求两个正整数的最大公约数

    辗转相除法 思路 1 将两整数求余 a b x 2 如果x 0 则b为最大公约数 3 如果x 0 则 a b b x 继续从1开始执行 4 也就是说该循环的是否继续的判断条件就是x是否为0 代码如下 def main a int input
  • javascript经典代码推荐

  • 基于Matlab的高精度轨道传播器模拟

    基于Matlab的高精度轨道传播器模拟 传播器模拟是一种常见的工程方法 用于预测和分析卫星 火箭或其他天体在轨道上的运动 在这篇文章中 我们将使用Matlab编写一个高精度轨道传播器模拟器 并提供相应的源代码 轨道传播器模拟器的主要目标是根
  • FRP服务器搭建成功后,配置多个客户端使用

    FRP内网穿透服务器搭建成功后 在服务器后台启动FRP 然后还需要两步 第一 在域名购买的网站 比如阿里云 配置一条所有子域名到服务器IP的规则 第二 配置多个客户端 A电脑的配置信息如下 common server addr 服务器IP
  • 前端八股文系列(四)4 JavaScript

    文章目录 前端八股文系列 四 4 JavaScript JS中的8种数据类型及区别 JS中的数据类型检测方案 1 typeof 2 instanceof 3 Object prototype toString call instanceof
  • LeetCode-1781. 所有子字符串美丽值之和【哈希表,字符串,计数】

    LeetCode 1781 所有子字符串美丽值之和 哈希表 字符串 计数 题目描述 解题思路一 简单暴力 双层循环 重点是分别记录子字符串 i j 的最大最小频率 注意这里当i变的时候 所有字符出现的频率就清理 否则在原来的基础上加就行 解
  • 栈的应用一之括号匹配问题

    括号匹配问题 给一个类似这样的字符串 char a abc 检测三种括号的左右括号是否匹配 分析 先取出一个字符 并判断是不是括号 任意括号 1 不是括号 取下一个字符 2 是括号 1 是左括号 压栈 2 是右括号 和栈顶元素比较 栈空 前
  • 教程:使用C#实现PDF文件和字节数组的相互转换

    字节数组有助于存储或传输数据 同样 PDF文件格式因其功能和兼容性而广受欢迎 可以使用C 语言将PDF文件转换为字节数组 也可以将字节数组转换为PDF文件 这可以帮助更有效地在数据库中存储和归档PDF文件 还可以通过使用字节数组来序列化数据
  • CMake中target_compile_definitions的使用

    CMake中的target compile definitions命令用于向target添加编译定义 其格式如下 target compile definitions
  • 什么是DDoS攻击?如何抵御DDos攻击?

    什么是DDoS攻击 如何抵御DDos攻击 单纯的土豆 2016 05 23 安全报道显示2015年DDoS攻击强度创下新纪录 那么DDoS到底是什么呢 了解一些 对产品经理与后台的同事沟通有好处 分布式拒绝服务 DDoS Distribut
  • mac 卸载 XCode

    1 卸载之前的XCode 命令行执行下面命令 sudo Developer Library uninstall devtools mode all sudo Developer Library uninstall developer fol
  • Springboot 集成 minio分享以及小坑 和 单机部署

    第一步先引入minio依赖
  • C#读取文本文件

    根据文件名到对应文件夹中读取对应文本文件 txt 并返回数据集合 使用流读取类StreamReader 一行一行读取 ReadLine 文本格式 public static List
  • 使用connect by进行级联查询

    connect by可以用于级联查询 常用于对具有树状结构的记录查询某一节点的所有子孙节点或所有祖辈节点 来看一个示例 现假设我们拥有一个菜单表t menu 其中只有三个字段 id name和parent id 它们是具有父子关系的 最顶级