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'