connect by超乎你想象

SQL中的connect by主要用在层级关系的查询,乍看确实可能有些绕,但在某些场景下,确实方便,语法格式如下,

{ CONNECT BY [ NOCYCLE ] condition [AND condition]...
[ START WITH condition ] | START WITH condition 
CONNECT BY [ NOCYCLE ] condition [AND condition]...}

关键字的解释,

start with:指定起始节点的条件。
connect by:指定条件关系。
prior:查询上级行的限定符,格式:prior column1 = column2 or column1 = prior column2 and …。
nocycle:若数据表中存在循环行,那么不添加此关键字会报错,添加关键字后,便不会报错,但循环的两行只会显示其中的第一条循环行:该行只有一个子行,而且子行又是该行的祖先行。
connect_by_iscycle:前置条件:在使用了nocycle之后才能使用此关键字,用于表示是否是循环行,0表示否,1表示是。
connect_by_isleaf:是否是叶子节点,0表示否,1表示是。
level:level伪列,表示层级,值越小层级越高,level=1为层级最高节点。

创建测试表,id列表示序号,lead_id表示上级id,name是名称,salary是当前name的薪水,

SQL> create table test(          
  2  id number,
  3  lead_id number,           
  4  name varchar2(30), 
  5  salary number);
  
  SQL> select * from test;
        ID    LEAD_ID NAME                               SALARY
---------- ---------- ------------------------------ ----------
         1          0 a                                  100000
         2          1 b1                                  50000
         3          1 b2                                  50000
         4          2 c1                                  20000
         5          2 c2                                  20000
         6          3 c3                                  20000
         7          3 c4                                  20000

将上面的数据,转换成这个结构,看起来更加清晰,a是大Boss,因此lead_id是0,月薪10万,b1和b2是a管辖的两个部门Boss,他们的lead_id是a的id=1,b1和b2的月薪是5万,c1和c2是b1管辖部门的员工,因此他们的lead_id是b1的id=2,c3和c4是b2管辖部门的员工,因此他们的lead_id是b2的id=3,c1-c4的月薪是2万,


示例1:

查询以lead_id为0开始的节点的所有直属节点,即查询从a开始所有管辖的员工信息,如下所示,priorname是该节点的上级,

SQL> select id, lead_id, name, prior name, salary from test
  2  start with lead_id = 0
  3  connect by prior id = lead_id;                              
        ID    LEAD_ID NAME                           PRIORNAME                          SALARY
---------- ---------- ------------------------------ ------------------------------ ----------
         1          0 a                                                                 100000
         2          1 b1                             a                                   50000
         4          2 c1                             b1                                  20000
         5          2 c2                             b1                                  20000
         3          1 b2                             a                                   50000
         6          3 c3                             b2                                  20000
         7          3 c4                             b2                                  20000

等价于以id为1开始的节点的所有直属节点,

SQL> select id, lead_id, name, prior name, salary from test
  2  start with id = 1 
  3  connect by prior id = lead_id;
        ID    LEAD_ID NAME                           PRIORNAME                          SALARY
---------- ---------- ------------------------------ ------------------------------ ----------
         1          0 a                                                                 100000
         2          1 b1                             a                                   50000
         4          2 c1                             b1                                  20000
         5          2 c2                             b1                                  20000
         3          1 b2                             a                                   50000
         6          3 c3                             b2                                  20000
         7          3 c4                             b2                                  20000

示例2:

查询以id为6开始的节点的所有直属节点,从图中可知,id=6是c3,他只有上级,没有下级,因此只是c3这条,

SQL> select id, lead_id, name, prior name, salary from test
  2  start with id = 6
  3  connect by prior id = lead_id;
        ID    LEAD_ID NAME                           PRIORNAME                          SALARY
---------- ---------- ------------------------------ ------------------------------ ----------
         6          3 c3                                                                 20000

示例3:

如下语句,还是以id为6开始,但是prior是lead_id=id,不是上述prior id=lead_id,看下返回信息,则是c3以及他的所有直属上级,

SQL> select id, lead_id, name, prior name, salary from test                 
  2  start with id = 6
  3  connect by prior lead_id = id;
        ID    LEAD_ID NAME                           PRIORNAME                          SALARY
---------- ---------- ------------------------------ ------------------------------ ----------
         6          3 c3                                                                 20000
         3          1 b2                             c3                                  50000
         1          0 a                              b2                                 100000

官方文档提到了,prior关键字可以位于操作符的任意一侧,

PRIOR is most commonly used when comparing column values with the equality operator. (The PRIOR keyword can be on either side of the operator.)

但是,从prior lead_id=id和prior id=lead_id的区别,你能想到什么?

没错,是树的遍历方向,这就是所谓神奇的地方。

(1) 如示例2,prior放在子节点端,则表示扫描树是以start with指定的节点作为根节点从上往下扫描。可能对应一个或多个分支。start with可以省略,如果省略,表示对所有节点都当成根节点分别进行遍历。
2)如示例3,prior放在上级节点端,则表示扫描树是以start with指定的节点作为最低层子节点,从下往上扫描。顺序是子节点往上级节点扫描,直到根节点为止,这种情况只能得到一个分支。start with可以省略,如果省略,表示对所有节点都当成最低层子节点分别往根节点方向遍历。

为了看得更直观,通过lpad函数、level伪列,格式化层级,可以看到a是第一层级,b1和b2是第二层级,其他是第三层级,

select id, lead_id, lpad(' ', level*2, ' ')||name, prior name, salary, connect_by_isleaf from test
start with lead_id = 0
connect by prior id = lead_id;


以上介绍的,其实只是connect by一些最基本的使用,connect by的用法上,远不止这些,通过几行SQL,能实现更加复杂的场景。

说句题外话,不积跬步无以至千里,争取我们都能做到月入10万的大Boss,就从关注我的公众号开始。

参考文献:

https://www.cnblogs.com/wanggang-java/p/10916426.html#_label2

https://docs.oracle.com/en/database/oracle/oracle-database/20/sqlrf/Hierarchical-Query-Operators.html#GUID-95F6A554-C6FE-42CD-88A6-7A1C162ED964

近期热文:

公众号600篇文章分类和索引

Oracle ACE,一段不可思议的旅程

Oracle 19c之RPM安装

应用执行慢的问题排查路径

ACOUG年会感想

千万级表数据更新的需求

探寻大表删除字段慢的原因

一次Oracle bug的故障排查过程思考

新增字段的一点一滴技巧

对recursive calls的深刻理解

《Oracle Concept》第三章 - 12

一次惊心动魄的问题排查

Java日期中“y”和“Y”的区别

英超梦幻之行

藤子不二雄博物馆之行

传控Tiki-Taka战术解惑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值