zoukankan      html  css  js  c++  java
  • Oracle的层次查询(oracle树查询)

    备注:测试数据库版本为Oracle 11g R2

    这个blog我们来聊聊Oracle的层次查询

    级联查询的一些语法:

    select … from tablename
    start with 条件1
    connect by 条件2
    where 条件3;
    

    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为层级最高节点

    如需要scott用户下建表及录入数据语句,可参考:
    scott建表及录入数据sql脚本

    这里,我们熟悉下emp的表结构
    emp表中每个员工都有一个mgr(上级领导,最顶层的没有)
    每个mgr其实也是emp表中的一员,通过父子ID关联,可以构造员工表的上下级关系

    一.start with ...... connect by语句

    显示员工表各个职员的级别
    因为职位为PRESIDENT的mgr是空的,可以认为是最高的一级

    有两种显示 上下级关系的办法,一种是通过 prior的顺序,一个是通过 parent key和child key 调换顺序。
    --找到起始职位的下级以及下级的下级
    connect by prior empno= mgr
    connect by mgr = prior empno
    --找到起始职位的上级及上级的上级
    connect by prior mgr= empno
    connect by empno = prior mgr

    select empno, ename, job, mgr, deptno, level from emp start with job='PRESIDENT' connect by prior empno= mgr;
    select empno, ename, job, mgr, deptno, level from emp start with job='PRESIDENT' connect by  mgr = prior empno;
    
    SQL> select empno, ename, job, mgr, deptno, level from emp start with job='PRESIDENT' connect by prior empno= mgr;
    EMPNO ENAME      JOB         MGR DEPTNO      LEVEL
    ----- ---------- --------- ----- ------ ----------
     7839 KING       PRESIDENT           10          1
     7566 JONES      MANAGER    7839     20          2
     7788 SCOTT      ANALYST    7566     20          3
     7876 ADAMS      CLERK      7788     20          4
     7902 FORD       ANALYST    7566     20          3
     7369 SMITH      CLERK      7902     20          4
     7698 BLAKE      MANAGER    7839     30          2
     7499 ALLEN      SALESMAN   7698     30          3
     7521 WARD       SALESMAN   7698     30          3
     7654 MARTIN     SALESMAN   7698     30          3
     7844 TURNER     SALESMAN   7698     30          3
     7900 JAMES      CLERK      7698     30          3
     7782 CLARK      MANAGER    7839     10          2
     7934 MILLER     CLERK      7782     10          3
    14 rows selected
    
    SQL> select empno, ename, job, mgr, deptno, level from emp start with job='PRESIDENT' connect by  mgr = prior empno;
    EMPNO ENAME      JOB         MGR DEPTNO      LEVEL
    ----- ---------- --------- ----- ------ ----------
     7839 KING       PRESIDENT           10          1
     7566 JONES      MANAGER    7839     20          2
     7788 SCOTT      ANALYST    7566     20          3
     7876 ADAMS      CLERK      7788     20          4
     7902 FORD       ANALYST    7566     20          3
     7369 SMITH      CLERK      7902     20          4
     7698 BLAKE      MANAGER    7839     30          2
     7499 ALLEN      SALESMAN   7698     30          3
     7521 WARD       SALESMAN   7698     30          3
     7654 MARTIN     SALESMAN   7698     30          3
     7844 TURNER     SALESMAN   7698     30          3
     7900 JAMES      CLERK      7698     30          3
     7782 CLARK      MANAGER    7839     10          2
     7934 MILLER     CLERK      7782     10          3
    14 rows selected
    

    用lpad进行填充,看起来更有层次感

    select empno, cast(lpad(' ', level*2-1,' ')||ename as varchar2(20)) ename, job, mgr, deptno, level 
    from emp 
    start with job='PRESIDENT' connect by prior empno= mgr;
    
    SQL> select empno, cast(lpad(' ', level*2-1,' ')||ename as varchar2(20)) ename, job, mgr, deptno, level from emp start with job='PRESIDENT' connect by prior empno= mgr;
    EMPNO ENAME                JOB         MGR DEPTNO      LEVEL
    ----- -------------------- --------- ----- ------ ----------
     7839  KING                PRESIDENT           10          1
     7566    JONES             MANAGER    7839     20          2
     7788      SCOTT           ANALYST    7566     20          3
     7876        ADAMS         CLERK      7788     20          4
     7902      FORD            ANALYST    7566     20          3
     7369        SMITH         CLERK      7902     20          4
     7698    BLAKE             MANAGER    7839     30          2
     7499      ALLEN           SALESMAN   7698     30          3
     7521      WARD            SALESMAN   7698     30          3
     7654      MARTIN          SALESMAN   7698     30          3
     7844      TURNER          SALESMAN   7698     30          3
     7900      JAMES           CLERK      7698     30          3
     7782    CLARK             MANAGER    7839     10          2
     7934      MILLER          CLERK      7782     10          3
    14 rows selected
    

    也可以从下往上查找
    查看雇员scott及其上级的相关信息

    select empno, cast(lpad(' ', level*2-1,' ')||ename as varchar2(20)) ename, job, mgr, deptno, level 
    from emp 
    start with empno=7788 connect by empno= prior mgr;
    
    SQL> select empno, cast(lpad(' ', level*2-1,' ')||ename as varchar2(20)) ename, job, mgr, deptno, level from emp start with empno=7788 connect by empno= prior mgr;
    EMPNO ENAME                JOB         MGR DEPTNO      LEVEL
    ----- -------------------- --------- ----- ------ ----------
     7788  SCOTT               ANALYST    7566     20          1
     7566    JONES             MANAGER    7839     20          2
     7839      KING            PRESIDENT           10          3
    

    二.sys_connect_by_path函数

    --语法:SYS_CONNECT_BY_PATH ( column , char )
    --column和char可以是VARCHAR2、CHAR、NCHAR,、 NVARCHAR2中的一种
    --返回值是VARCHAR2
    --返回值的字符集是与column的字符集一样的
    --只能用于层次查询中

    从名为KING的雇员开始,查出所有下级雇员相关信息以及雇员名路径(用'/'连接)

    select empno,
           cast(lpad(' ', level * 2 - 1, ' ') || ename as varchar2(20)) ename,
           job,
           mgr,
           deptno,
           level,
           cast(sys_connect_by_path(ename, '/') as varchar2(40)) namepath
      from emp
     start with ename = 'KING'
    connect by prior empno = mgr;
    
    SQL> select empno,
      2         cast(lpad(' ', level * 2 - 1, ' ') || ename as varchar2(20)) ename,
      3         job,
      4         mgr,
      5         deptno,
      6         level,
      7         cast(sys_connect_by_path(ename, '/') as varchar2(40)) namepath
      8    from emp
      9   start with ename = 'KING'
     10  connect by prior empno = mgr;
    EMPNO ENAME                JOB         MGR DEPTNO      LEVEL NAMEPATH
    ----- -------------------- --------- ----- ------ ---------- ----------------------------------------
     7839  KING                PRESIDENT           10          1 /KING
     7566    JONES             MANAGER    7839     20          2 /KING/JONES
     7788      SCOTT           ANALYST    7566     20          3 /KING/JONES/SCOTT
     7876        ADAMS         CLERK      7788     20          4 /KING/JONES/SCOTT/ADAMS
     7902      FORD            ANALYST    7566     20          3 /KING/JONES/FORD
     7369        SMITH         CLERK      7902     20          4 /KING/JONES/FORD/SMITH
     7698    BLAKE             MANAGER    7839     30          2 /KING/BLAKE
     7499      ALLEN           SALESMAN   7698     30          3 /KING/BLAKE/ALLEN
     7521      WARD            SALESMAN   7698     30          3 /KING/BLAKE/WARD
     7654      MARTIN          SALESMAN   7698     30          3 /KING/BLAKE/MARTIN
     7844      TURNER          SALESMAN   7698     30          3 /KING/BLAKE/TURNER
     7900      JAMES           CLERK      7698     30          3 /KING/BLAKE/JAMES
     7782    CLARK             MANAGER    7839     10          2 /KING/CLARK
     7934      MILLER          CLERK      7782     10          3 /KING/CLARK/MILLER
    14 rows selected
    

    三.connect_by_root 语句

    connect_by_root 查找根节点

    select empno,
           cast(lpad(' ', level * 2 - 1, ' ') || ename as varchar2(20)) ename,
           job,
           mgr,
           deptno,
           level,
           cast(sys_connect_by_path(ename, '/') as varchar2(40)) namepath,
           connect_by_root(empno) rootno
      from emp
     start with ename in ('JONES','BLAKE','CLARK')
    connect by prior empno = mgr;
    
    SQL> select empno,
      2         cast(lpad(' ', level * 2 - 1, ' ') || ename as varchar2(20)) ename,
      3         job,
      4         mgr,
      5         deptno,
      6         level,
      7         cast(sys_connect_by_path(ename, '/') as varchar2(40)) namepath,
      8         connect_by_root(empno) rootno
      9    from emp
     10   start with ename in ('JONES','BLAKE','CLARK')
     11  connect by prior empno = mgr;
    EMPNO ENAME                JOB         MGR DEPTNO      LEVEL NAMEPATH                                     ROOTNO
    ----- -------------------- --------- ----- ------ ---------- ---------------------------------------- ----------
     7566  JONES               MANAGER    7839     20          1 /JONES                                         7566
     7788    SCOTT             ANALYST    7566     20          2 /JONES/SCOTT                                   7566
     7876      ADAMS           CLERK      7788     20          3 /JONES/SCOTT/ADAMS                             7566
     7902    FORD              ANALYST    7566     20          2 /JONES/FORD                                    7566
     7369      SMITH           CLERK      7902     20          3 /JONES/FORD/SMITH                              7566
     7698  BLAKE               MANAGER    7839     30          1 /BLAKE                                         7698
     7499    ALLEN             SALESMAN   7698     30          2 /BLAKE/ALLEN                                   7698
     7521    WARD              SALESMAN   7698     30          2 /BLAKE/WARD                                    7698
     7654    MARTIN            SALESMAN   7698     30          2 /BLAKE/MARTIN                                  7698
     7844    TURNER            SALESMAN   7698     30          2 /BLAKE/TURNER                                  7698
     7900    JAMES             CLERK      7698     30          2 /BLAKE/JAMES                                   7698
     7782  CLARK               MANAGER    7839     10          1 /CLARK                                         7782
     7934    MILLER            CLERK      7782     10          2 /CLARK/MILLER                                  7782
    13 rows selected
    

    四.NOCYCLE、CONNECT_BY_ISCYCLE语句

    测试数据:
    备注:改动点,原表emp表职位为PRESIDENT的mgr是空,是最高级的领导
    这个地方,我把他的mgr设置为SMITH的empno 7369

    drop table emp2;
    
    create table EMP2
    (
      empno    NUMBER(4) not null,
      ename    VARCHAR2(10),
      job      VARCHAR2(9),
      mgr      NUMBER(4),
      hiredate DATE,
      sal      NUMBER(7,2),
      comm     NUMBER(7,2),
      deptno   NUMBER(2)
    );
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7369', 'SMITH', 'CLERK', '7902', to_date('17-12-1980', 'dd-mm-yyyy'), '800', null, '20');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7499', 'ALLEN', 'SALESMAN', '7698', to_date('20-02-1981', 'dd-mm-yyyy'), '1600', '300', '30');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7521', 'WARD', 'SALESMAN', '7698', to_date('22-02-1981', 'dd-mm-yyyy'), '1250', '500', '30');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7566', 'JONES', 'MANAGER', '7839', to_date('02-04-1981', 'dd-mm-yyyy'), '2975', null, '20');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7654', 'MARTIN', 'SALESMAN', '7698', to_date('28-09-1981', 'dd-mm-yyyy'), '1250', '1400', '30');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7698', 'BLAKE', 'MANAGER', '7839', to_date('01-05-1981', 'dd-mm-yyyy'), '2850', null, '30');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7782', 'CLARK', 'MANAGER', '7839', to_date('09-06-1981', 'dd-mm-yyyy'), '2450', null, '10');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7788', 'SCOTT', 'ANALYST', '7566', to_date('13-06-0187', 'dd-mm-yyyy'), '3000', null, '20');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7839', 'KING', 'PRESIDENT', 7369, to_date('17-11-1981', 'dd-mm-yyyy'), '5000', null, '10');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7844', 'TURNER', 'SALESMAN', '7698', to_date('08-09-1981', 'dd-mm-yyyy'), '1500', '0', '30');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7876', 'ADAMS', 'CLERK', '7788', to_date('13-06-0187', 'dd-mm-yyyy'), '1100', null, '20');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7900', 'JAMES', 'CLERK', '7698', to_date('03-12-1981', 'dd-mm-yyyy'), '950', null, '30');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7902', 'FORD', 'ANALYST', '7566', to_date('03-12-1981', 'dd-mm-yyyy'), '3000', null, '20');
    
    insert into emp2 (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO)
    values ('7934', 'MILLER', 'CLERK', '7782', to_date('23-01-1982', 'dd-mm-yyyy'), '1300', null, '10');
    commit;
    

    运行最上面的sql,只是将包名从emp调整为emp2
    此时报错,提示connect by循环了

    SELECT empno, ename, job, mgr, deptno, LEVEL
      FROM emp2
     START WITH job = 'PRESIDENT'
    CONNECT BY PRIOR empno = mgr;
    
    SQL> SELECT empno, ename, job, mgr, deptno, LEVEL
      2    FROM emp2
      3   START WITH job = 'PRESIDENT'
      4  CONNECT BY PRIOR empno = mgr;
    SELECT empno, ename, job, mgr, deptno, LEVEL
      FROM emp2
     START WITH job = 'PRESIDENT'
    CONNECT BY PRIOR empno = mgr
    ORA-01436: 用户数据中的 CONNECT BY 循环
    
    

    加上 nocycle,让语句不循环,就可以正常运行了
    CONNECT_BY_ISCYCLE 为1表示是循环的点

    SELECT empno, ename, job, mgr, deptno, LEVEL,CONNECT_BY_ISCYCLE iscycle
      FROM emp2
     START WITH job = 'PRESIDENT'
    CONNECT BY nocycle PRIOR empno = mgr;
    
    SQL> SELECT empno, ename, job, mgr, deptno, LEVEL,CONNECT_BY_ISCYCLE iscycle
      2    FROM emp2
      3   START WITH job = 'PRESIDENT'
      4  CONNECT BY nocycle PRIOR empno = mgr;
    EMPNO ENAME      JOB         MGR DEPTNO      LEVEL    ISCYCLE
    ----- ---------- --------- ----- ------ ---------- ----------
     7839 KING       PRESIDENT  7369     10          1          0
     7566 JONES      MANAGER    7839     20          2          0
     7788 SCOTT      ANALYST    7566     20          3          0
     7876 ADAMS      CLERK      7788     20          4          0
     7902 FORD       ANALYST    7566     20          3          0
     7369 SMITH      CLERK      7902     20          4          1
     7698 BLAKE      MANAGER    7839     30          2          0
     7499 ALLEN      SALESMAN   7698     30          3          0
     7521 WARD       SALESMAN   7698     30          3          0
     7654 MARTIN     SALESMAN   7698     30          3          0
     7844 TURNER     SALESMAN   7698     30          3          0
     7900 JAMES      CLERK      7698     30          3          0
     7782 CLARK      MANAGER    7839     10          2          0
     7934 MILLER     CLERK      7782     10          3          0
    14 rows selected
    

    五.connect_by_isleaf语句

    connect_by_isleaf 是否叶子节点,1为是,0为否
    叶子节点可以理解为最后一层

    select empno,
           cast(lpad(' ', level * 2 - 1, ' ') || ename as varchar2(20)) ename,
           job,
           mgr,
           deptno,
           level,
           cast(sys_connect_by_path(ename, '/') as varchar2(40)) namepath,
           CONNECT_BY_ISLEAF ISLEAF
      from emp
     start with ename = 'KING'
    connect by prior empno = mgr;
    
    SQL> select empno,
      2         cast(lpad(' ', level * 2 - 1, ' ') || ename as varchar2(20)) ename,
      3         job,
      4         mgr,
      5         deptno,
      6         level,
      7         cast(sys_connect_by_path(ename, '/') as varchar2(40)) namepath,
      8         CONNECT_BY_ISLEAF ISLEAF
      9    from emp
     10   start with ename = 'KING'
     11  connect by prior empno = mgr;
    EMPNO ENAME                JOB         MGR DEPTNO      LEVEL NAMEPATH                                     ISLEAF
    ----- -------------------- --------- ----- ------ ---------- ---------------------------------------- ----------
     7839  KING                PRESIDENT           10          1 /KING                                             0
     7566    JONES             MANAGER    7839     20          2 /KING/JONES                                       0
     7788      SCOTT           ANALYST    7566     20          3 /KING/JONES/SCOTT                                 0
     7876        ADAMS         CLERK      7788     20          4 /KING/JONES/SCOTT/ADAMS                           1
     7902      FORD            ANALYST    7566     20          3 /KING/JONES/FORD                                  0
     7369        SMITH         CLERK      7902     20          4 /KING/JONES/FORD/SMITH                            1
     7698    BLAKE             MANAGER    7839     30          2 /KING/BLAKE                                       0
     7499      ALLEN           SALESMAN   7698     30          3 /KING/BLAKE/ALLEN                                 1
     7521      WARD            SALESMAN   7698     30          3 /KING/BLAKE/WARD                                  1
     7654      MARTIN          SALESMAN   7698     30          3 /KING/BLAKE/MARTIN                                1
     7844      TURNER          SALESMAN   7698     30          3 /KING/BLAKE/TURNER                                1
     7900      JAMES           CLERK      7698     30          3 /KING/BLAKE/JAMES                                 1
     7782    CLARK             MANAGER    7839     10          2 /KING/CLARK                                       0
     7934      MILLER          CLERK      7782     10          3 /KING/CLARK/MILLER                                1
    14 rows selected
    

    六.connect by构造数据

    --connect by构造等差数列
    select rownum rn from dual connect by rownum<5;
    --调整起始值
    select rownum+15 rn from dual connect by rownum<5;
    --调整间隔
    select 3*rownum-9 rn from dual connect by rownum<5;
    --等比数列
    select power(2,rownum) rn from dual connect by rownum<5;
    
    SQL> --connect by构造等差数列
    SQL> select rownum rn from dual connect by rownum<5;
            RN
    ----------
             1
             2
             3
             4
    SQL> --调整起始值
    SQL> select rownum+15 rn from dual connect by rownum<5;
            RN
    ----------
            16
            17
            18
            19
    SQL> --调整间隔
    SQL> select 3*rownum-9 rn from dual connect by rownum<5;
            RN
    ----------
            -6
            -3
             0
             3
    SQL> --等比数列
    SQL> select power(2,rownum) rn from dual connect by rownum<5;
            RN
    ----------
             2
             4
             8
            16
    
    --用connect by构造 26个英文字母
    with t as (select ascii('A')+rownum-1 aa from dual connect by rownum<=26),
     u as (select chr(aa) from t)
    select * from u;
    
    SQL> --用connect by构造 26个英文字母
    SQL> with t as (select ascii('A')+rownum-1 aa from dual connect by rownum<=26),
      2   u as (select chr(aa) from t)
      3  select * from u;
    CHR(AA)
    -------
    A
    B
    C
    D
    E
    F
    G
    H
    I
    J
    K
    L
    M
    N
    O
    P
    Q
    R
    S
    T
    U
    V
    W
    X
    Y
    Z
    26 rows selected
    

    百钱买鸡兔
    小母鸡4块钱5只
    老母鸡3块钱1只
    大白兔2块钱1只
    小白兔3块钱4只

    花费的钱总数是100
    小母鸡、老母鸡、大白兔、小白兔的总数也刚好是100

    with tmp1 as
     (select rownum n from dual connect by rownum <= 100-3)
    select t1.n xmj, t2.n lmj, t3.n dbt, t4.n xbt
        from tmp1 t1, tmp1 t2, tmp1 t3, tmp1 t4
        where 1 = 1
        and   t1.n*4/5 + t2.n*3/1 + t3.n*2/1 + t4.n*3/4 = 100
        and   t1.n + t2.n + t3.n + t4.n = 100;
    
    SQL> with tmp1 as
      2   (select rownum n from dual connect by rownum <= 100-3)
      3  select t1.n xmj, t2.n lmj, t3.n dbt, t4.n xbt
      4      from tmp1 t1, tmp1 t2, tmp1 t3, tmp1 t4
      5      where 1 = 1
      6      and   t1.n*4/5 + t2.n*3/1 + t3.n*2/1 + t4.n*3/4 = 100
      7      and   t1.n + t2.n + t3.n + t4.n = 100;
      
           XMJ        LMJ        DBT        XBT
    ---------- ---------- ---------- ----------
             5          1         18         76
             5          6          9         80
            10          2         16         72
            10          7          7         76
            15          3         14         68
            15          8          5         72
            20          4         12         64
            20          9          3         68
            25          5         10         60
            25         10          1         64
            30          1         17         52
            30          6          8         56
            35          2         15         48
            35          7          6         52
            40          3         13         44
            40          8          4         48
            45          4         11         40
            45          9          2         44
            50          5          9         36
            55          1         16         28
            55          6          7         32
            60          2         14         24
            60          7          5         28
            65          3         12         20
            65          8          3         24
            70          4         10         16
            70          9          1         20
            75          5          8         12
            80          1         15          4
            80          6          6          8
            85          7          4          4
    31 rows selected


    作者:只是甲
    链接:https://www.jianshu.com/p/5225a48b4754
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    spring六种种依赖注入方式
    HibernateDaoSupport 源码
    ServiceStack.Redis常用操作
    ServiceStack.Redis 之 IRedisTypedClient
    ServiceStack.Redis之IRedisClient
    Windows下Redis的安装使用
    为什么要使用SLF4J而不是Log4J
    每天一个linux命令:mkdir
    浅谈Redis及其安装配置
    Solr4.4的安装与配置
  • 原文地址:https://www.cnblogs.com/gered/p/12841805.html
Copyright © 2011-2022 走看看