使用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 数据透视表中的列

    示例选择 select from select 1 cnt 2 sm 55 name 12 month 2011 year 12 2011 mnth txt from dual union all select 1 cnt 2 sm 54
  • 如何终止正在运行的 SELECT 语句

    如何通过终止会话来停止正在运行的 SELECT 语句 该命令不断根据 SELECT 语句向我提供输出 我想在其间停止它 As you keep getting pages of results I m assuming you starte
  • 将游标中的数据合并为一个

    我有一个存储过程 它多次执行另一个存储过程 我需要联合并返回数据 这是在执行第二个过程后得到的 我可以以某种方式将多个游标中的数据合并到另一个游标中吗 没有临时表或类表数据类型是否可能 编辑 联合的游标计数实际上是 n 其中 n 是 1 2
  • Oracle Many OR 与 IN () 的 SQL 性能调优 [重复]

    这个问题在这里已经有答案了 我手头没有 解释计划 您能帮忙判断以下哪一个更有效吗 选项1 select from VIEW ABC where STRING COL AA OR STRING COL BB OR STRING COL BB
  • 用C++连接oracle数据库

    我正在寻找一种方法来连接到远程 Oracle 数据库并从 C 控制台应用程序中的表中读取一些数据 有人可以给我一些提示吗 谢谢 soci http soci sourceforge net http soci sourceforge net
  • 如何比较表中最后一个和倒数第二个条目的值?

    我在 Oracle 中有一个名为quotes 的表 其中包含两列 date 和value 我想比较表中最后一个条目和倒数第二个条目的值 在此示例中 我想获取日期13 1 和 11 1在一行中以及每个日期的值之间的差异 10 5 5 报价表
  • 如何列出表中的所有列?

    对于各种流行的数据库系统 如何列出表中的所有列 对于 MySQL 请使用 DESCRIBE name of table 只要您使用 SQL Plus 或 Oracle 的 SQL Developer 这也适用于 Oracle
  • 如何使用低权限的 PL-SQL 获取 Oracle 中的列数据类型?

    我对 Oracle 数据库中的一些表具有 只读 访问权限 我需要获取某些列的架构信息 我想使用类似于 MS SQL 的东西sp help 我看到此查询中列出了我感兴趣的表 SELECT FROM ALL TABLES 当我运行这个查询时 O
  • 在Oracle中查找不包含数字数据的行

    我试图在一个非常大的 Oracle 表中找到一些有问题的记录 即使该列是 varchar2 列 也应包含所有数值数据 我需要找到不包含数字数据的记录 当我尝试在此列上调用 to number col name 函数时 它会抛出错误 我想你可
  • 安装 OCI8:如何纠正“使用未定义常量 OCI_COMMIT_ON_SUCCESS”错误?

    我正在尝试在 RedHat 服务器 RHEL7 上为我的 Apache 服务器安装 OCI8 此时 当我尝试使用 Symphony 连接到我的服务器时 出现以下错误 异常 ErrorException 使用未定义的常量 OCI COMMIT
  • Oracle内置函数元数据

    有没有办法获取 Oracle 内置聚合和其他功能的元数据 例如AVG STDDEV SQRT ETC 我需要知道对象 id 和参数元 In the SYS ALL OBJECTS查看我找不到任何有用的东西 我也尝试过搜索SYS ALL AR
  • 如何修复“Oracle.EntityFrameworkCore 类型中的方法‘get_Info’没有实现”。

    我正在尝试通过 EW 连接到 Oracle DB 论方法OnConfiguring是错误 System TypeLoadException 程序集 Oracle EntityFrameworkCore Version 2 0 19 1 Cu
  • 在sql plus脚本中运行循环

    我正在 sql plus 中运行脚本 我的脚本中有一个 for 循环 BEGIN FOR count IN 1 100 LOOP INSERT INTO CompanyShare VALUES count 1 250 END LOOP EN
  • Hibernate 本机查询 - char(3) 列

    我在 Oracle 中有一个表 其中列 SC CUR CODE 是 CHAR 3 当我做 Query q2 em createNativeQuery select sc cur code sc amount from sector cost
  • Oracle - 获取星期几

    今天是星期二 为什么当我运行这个 SQL 语句时 它说今天不是星期二 SELECT CASE WHEN TO CHAR sysdate Day Tuesday THEN Its Tuesday ELSE Its Not Tuesday EN
  • 如何检查oracle数据库中分配给模式、角色的对象的权限(DDL、DML、DCL)?

    大多数时候 我们都在与愚蠢的事情作斗争 以获取架构 角色及其对象的权限详细信息 并尝试找到一些简单的方法来获取有关它的所有详细信息以及伪查询代码 以批量生成授予语句以供进一步使用执行 所以我们在这里得到它 关于数据字典视图前缀的一些简单介绍
  • 从 PL/SQL 调用 shell 脚本,但 shell 以 grid 用户而非 oracle 身份执行

    我正在尝试使用 Runtime getRuntime exec 从 Oracle 数据库内部执行 shell 脚本 在 Red Hat 5 5 上运行的 Oracle 11 2 0 4 EE CREATE OR REPLACE proced
  • 插入具有多个值的外键

    我想知道 是否有可能创建一个表 其中我有一个接受外键但同一行可能有多个值的表 例如 Employee id name skillid Skill Skillid skillname 这里 Employee 的一个例子可以是 Employee
  • Oracle SQL 函数中可以有 commit 语句吗

    在 SQL 函数中使用 COMMIT 语句是否可能 有意义 从技术上来说 答案是肯定的 你can请执行下列操作 create or replace function committest return number as begin upd
  • 在Oracle中使用IW和MM

    我使用 IW 表示每周结果 使用 MM 表示每月结果 但我总是收到错误 ORA 00979 not a GROUP BY expression 00979 00000 not a GROUP BY expression 我的疑问是这些 We

随机推荐

  • 数据库 --- 约束

    一 什么是约束 常见的约束有那些 约束是在创建表的时候 可以给表的字段添加相应的约束 添加约束的目的是为了保证表中数据的合法性 有效性 完整性 常见的约束有 非空约束 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 它们是具有父子关系的 最顶级