Oracle开发者中级第7课(层级查询)实验
概述
本实验参考DevGym中的实验指南。
创建环境
创建表,依赖于HR示例Schema中的employees表:
create table employees asselect * from hr.employeeswhere department_id in ( 90, 100, 60 );
查看数据:
SQL> select * from employees;EMPLOYEE_ID FIRST_NAME LAST_NAME EMAIL PHONE_NUMBER HIRE_DATE JOB_ID SALARY COMMISSION_PCT MANAGER_ID DEPARTMENT_ID
______________ ______________ ____________ ___________ _______________ ____________ _____________ _________ _________________ _____________ ________________103 Alexander Hunold AHUNOLD 590.423.4567 03-JAN-06 IT_PROG 9000 102 60104 Bruce Ernst BERNST 590.423.4568 21-MAY-07 IT_PROG 6000 103 60105 David Austin DAUSTIN 590.423.4569 25-JUN-05 IT_PROG 4800 103 60106 Valli Pataballa VPATABAL 590.423.4560 05-FEB-06 IT_PROG 4800 103 60107 Diana Lorentz DLORENTZ 590.423.5567 07-FEB-07 IT_PROG 4200 103 60100 Steven King SKING 515.123.4567 17-JUN-03 AD_PRES 24000 90101 Neena Kochhar NKOCHHAR 515.123.4568 21-SEP-05 AD_VP 17000 100 90102 Lex De Haan LDEHAAN 515.123.4569 13-JAN-01 AD_VP 17000 100 90108 Nancy Greenberg NGREENBE 515.124.4569 17-AUG-02 FI_MGR 12008 101 100109 Daniel Faviet DFAVIET 515.124.4169 16-AUG-02 FI_ACCOUNT 9000 108 100110 John Chen JCHEN 515.124.4269 28-SEP-05 FI_ACCOUNT 8200 108 100111 Ismael Sciarra ISCIARRA 515.124.4369 30-SEP-05 FI_ACCOUNT 7700 108 100112 Jose Manuel Urman JMURMAN 515.124.4469 07-MAR-06 FI_ACCOUNT 7800 108 100113 Luis Popp LPOPP 515.124.4567 07-DEC-07 FI_ACCOUNT 6900 108 10014 rows selected.
Connect By
Connect by是一种Oracle特有的使用SQL创建数据树的方法。它有两个关键子句,start with和connect by。
Start With指定树根,例如start with employee_id = 100
表示CEO,但更通用的方法是start with manager_id is null
,因为CEO上面没有manager了。
Connect By指定父子关系,这将链接存储父值和子值的列。您可以使用prior关键字从父行访问值。
例如:
select * from employees
start with manager_id is null
connect by prior employee_id = manager_id;
可以认为prior等同于parent,也就是父行的employee_id等于当前行的manager_id。
练习的答案如下,注意其将树做了颠倒:
select employee_id, first_name, last_name, manager_id
from employees
start with employee_id = 107
connect by prior manager_id = employee_id;EMPLOYEE_ID FIRST_NAME LAST_NAME MANAGER_ID
----------- -------------------- ------------------------- ----------107 Diana Lorentz 103103 Alexander Hunold 102102 Lex De Haan 100100 Steven King
Recursive With
Oracle Database 11.2引入了另一种访问树的方法,即recursive with,这实际上是ANSI的语法。
Recursive With由Base Query和Recursive Query组成,前者定义根,相当于start with,后者相当于connect by。
例如在以下查询中:
with org_chart (employee_id, first_name, last_name, manager_id
) as (select employee_id, first_name, last_name, manager_id from employeeswhere manager_id is nullunion allselect e.employee_id, e.first_name, e.last_name, e.manager_id from org_chart ocjoin employees eon e.manager_id = oc.employee_id
)select * from org_chart;EMPLOYEE_ID FIRST_NAME LAST_NAME MANAGER_ID
______________ ______________ ____________ _____________100 Steven King101 Neena Kochhar 100102 Lex De Haan 100108 Nancy Greenberg 101103 Alexander Hunold 102109 Daniel Faviet 108110 John Chen 108111 Ismael Sciarra 108112 Jose Manuel Urman 108113 Luis Popp 108104 Bruce Ernst 103105 David Austin 103106 Valli Pataballa 103107 Diana Lorentz 10314 rows selected.
Base query如下,其定义了根节点,即CEO:
select employee_id, first_name, last_name, manager_id from employeeswhere manager_id is null
Recursive Query如下,定义了父子关系:
select e.employee_id, e.first_name, e.last_name, e.manager_id from org_chart ocjoin employees eon e.manager_id = oc.employee_id
其它就是固定格式了,模仿就好。
练习的答案为,通过这个例子,可以对照Connect By写法:
with org_chart (employee_id, first_name, last_name, manager_id
) as (select employee_id, first_name, last_name, manager_id from employeeswhere employee_id = 107union allselect e.employee_id, e.first_name, e.last_name, e.manager_id from org_chart ocjoin employees eon e.employee_id = oc.manager_id
)select * from org_chart;EMPLOYEE_ID FIRST_NAME LAST_NAME MANAGER_ID
______________ _____________ ____________ _____________107 Diana Lorentz 103103 Alexander Hunold 102102 Lex De Haan 100100 Steven King
Level
如果能表示节点在书中的层级,就能知道哪些员工更资深,哪些在同一水平。
Connect By中的写法
首先来看Connect By方法。
level是一个伪列,表示树的深度,根为1,每下沉一级加1。
例如:
select level, employee_id, first_name, last_name, manager_id
from employees
start with manager_id is null
connect by prior employee_id = manager_id;LEVEL EMPLOYEE_ID FIRST_NAME LAST_NAME MANAGER_ID
________ ______________ ______________ ____________ _____________1 100 Steven King2 101 Neena Kochhar 1003 108 Nancy Greenberg 1014 109 Daniel Faviet 1084 110 John Chen 1084 111 Ismael Sciarra 1084 112 Jose Manuel Urman 1084 113 Luis Popp 1082 102 Lex De Haan 1003 103 Alexander Hunold 1024 104 Bruce Ernst 1034 105 David Austin 1034 106 Valli Pataballa 1034 107 Diana Lorentz 10314 rows selected.
再优化一下,可以用缩进表示层级,这样就更清晰了:
select level, employee_id, lpad ( ' ', level, ' ' ) || first_name || ' ' || last_name name, manager_id
from employees
start with manager_id is null
connect by prior employee_id = manager_id;LEVEL EMPLOYEE_ID NAME MANAGER_ID
________ ______________ ________________________ _____________1 100 Steven King2 101 Neena Kochhar 1003 108 Nancy Greenberg 1014 109 Daniel Faviet 1084 110 John Chen 1084 111 Ismael Sciarra 1084 112 Jose Manuel Urman 1084 113 Luis Popp 1082 102 Lex De Haan 1003 103 Alexander Hunold 1024 104 Bruce Ernst 1034 105 David Austin 1034 106 Valli Pataballa 1034 107 Diana Lorentz 10314 rows selected.
Recursive With中的写法
由于没有level伪列,必须自己构建了:
with org_chart (employee_id, first_name, last_name, manager_id, lvl
) as (select employee_id, first_name, last_name, manager_id, 1 lvlfrom employeeswhere manager_id is nullunion allselect e.employee_id, e.first_name, e.last_name, e.manager_id, oc.lvl + 1from org_chart ocjoin employees eon e.manager_id = oc.employee_id
)select * from org_chart;EMPLOYEE_ID FIRST_NAME LAST_NAME MANAGER_ID LVL
______________ ______________ ____________ _____________ ______100 Steven King 1101 Neena Kochhar 100 2102 Lex De Haan 100 2108 Nancy Greenberg 101 3103 Alexander Hunold 102 3109 Daniel Faviet 108 4110 John Chen 108 4111 Ismael Sciarra 108 4112 Jose Manuel Urman 108 4113 Luis Popp 108 4104 Bruce Ernst 103 4105 David Austin 103 4106 Valli Pataballa 103 4107 Diana Lorentz 103 414 rows selected.
Sorting Output: Connect By
构建分层查询时,数据库会按照与树结构匹配的顺序返回行。
Connect by 以深度优先搜索顺序返回行。 常规的order by无法使用。但是您可以保留深度优先树并对具有相同父级的行进行排序。 您可以使用 order by 的sibling子句执行此操作。
因此,要在他们之后显示经理的报告,按雇用日期(从头到尾)对同一经理的员工进行排序,您可以执行以下操作:
select level, employee_id, lpad ( ' ', level, ' ' ) || first_name || ' ' || last_name name, hire_date, manager_id
from employees
start with manager_id is null
connect by prior employee_id = manager_id
order siblings by hire_date;LEVEL EMPLOYEE_ID NAME HIRE_DATE MANAGER_ID
________ ______________ ________________________ ____________ _____________1 100 Steven King 17-JUN-032 102 Lex De Haan 13-JAN-01 1003 103 Alexander Hunold 03-JAN-06 1024 105 David Austin 25-JUN-05 1034 106 Valli Pataballa 05-FEB-06 1034 107 Diana Lorentz 07-FEB-07 1034 104 Bruce Ernst 21-MAY-07 1032 101 Neena Kochhar 21-SEP-05 1003 108 Nancy Greenberg 17-AUG-02 1014 109 Daniel Faviet 16-AUG-02 1084 110 John Chen 28-SEP-05 1084 111 Ismael Sciarra 30-SEP-05 1084 112 Jose Manuel Urman 07-MAR-06 1084 113 Luis Popp 07-DEC-07 10814 rows selected.
Sorting Output: Recursive With
Recursive with可以定义遍历是深度优先还是广度优先。
Depth-First Search
深度优先的概念参见这里。下图来自维基百科:
with org_chart (employee_id, first_name, last_name, hire_date, manager_id, lvl
) as (select employee_id, first_name, last_name, hire_date, manager_id, 1 lvlfrom employeeswhere manager_id is nullunion allselect e.employee_id, e.first_name, e.last_name, e.hire_date, e.manager_id, oc.lvl + 1from org_chart ocjoin employees eon e.manager_id = oc.employee_id
) search depth first by hire_date set hire_seqselect employee_id, lpad ( ' ', lvl, ' ' ) || first_name || ' ' || last_name name, hire_date, manager_id, lvl from org_chartorder by hire_seq;EMPLOYEE_ID NAME HIRE_DATE MANAGER_ID LVL
______________ ________________________ ____________ _____________ ______100 Steven King 17-JUN-03 1102 Lex De Haan 13-JAN-01 100 2103 Alexander Hunold 03-JAN-06 102 3105 David Austin 25-JUN-05 103 4106 Valli Pataballa 05-FEB-06 103 4107 Diana Lorentz 07-FEB-07 103 4104 Bruce Ernst 21-MAY-07 103 4101 Neena Kochhar 21-SEP-05 100 2108 Nancy Greenberg 17-AUG-02 101 3109 Daniel Faviet 16-AUG-02 108 4110 John Chen 28-SEP-05 108 4111 Ismael Sciarra 30-SEP-05 108 4112 Jose Manuel Urman 07-MAR-06 108 4113 Luis Popp 07-DEC-07 108 414 rows selected.
Breadth-First Search
广度优先的概念见这里。下图来自维基百科:
只需将depth换成breadth,其它不变。结果很完美,缩进很给力:
with org_chart (employee_id, first_name, last_name, hire_date, manager_id, lvl
) as (select employee_id, first_name, last_name, hire_date, manager_id, 1 lvlfrom employeeswhere manager_id is nullunion allselect e.employee_id, e.first_name, e.last_name, e.hire_date, e.manager_id, oc.lvl + 1from org_chart ocjoin employees eon e.manager_id = oc.employee_id
) search breadth first by hire_date set hire_seqselect employee_id, lpad ( ' ', lvl, ' ' ) || first_name || ' ' || last_name name, hire_date, manager_id, lvl from org_chartorder by hire_seq;EMPLOYEE_ID NAME HIRE_DATE MANAGER_ID LVL
______________ ________________________ ____________ _____________ ______100 Steven King 17-JUN-03 1102 Lex De Haan 13-JAN-01 100 2101 Neena Kochhar 21-SEP-05 100 2108 Nancy Greenberg 17-AUG-02 101 3103 Alexander Hunold 03-JAN-06 102 3109 Daniel Faviet 16-AUG-02 108 4105 David Austin 25-JUN-05 103 4110 John Chen 28-SEP-05 108 4111 Ismael Sciarra 30-SEP-05 108 4106 Valli Pataballa 05-FEB-06 103 4112 Jose Manuel Urman 07-MAR-06 108 4107 Diana Lorentz 07-FEB-07 103 4104 Bruce Ernst 21-MAY-07 103 4113 Luis Popp 07-DEC-07 108 414 rows selected.
Detecting Loops
实际上,本文讨论的层级架构指的是类似于组织架构中这种单线管理的结构。如果是族谱,每个子节点会有两个父节点(父亲和母亲),这样会导致死循环。另外,数据的问题也可能导致死循环,因此有时需要避免这种问题。
例如,将CEO的经理设为107号员工:
update employees
set manager_id = 107
where employee_id = 100;
此时,系统会检测到循环并报错,因为同一节点被多次访问:
select * from employees
start with employee_id = 100
connect by prior employee_id = manager_id;Error starting at line : 1 in command -
select * from employees
start with employee_id = 100
connect by prior employee_id = manager_id
Error report -
ORA-01436: CONNECT BY loop in user data
Connect By方法
此时可以使用nocycle子句,当重复访问同一节点时会不显示它,不会报错,然后继续遍历:
select * from employees
start with employee_id = 100
connect by nocycle prior employee_id = manager_id;
Recursive With方法
倒也简单,就是使用如下的语法,即检测employee_id是否有重复访问,如果有,则标为Y:
cycle employee_id set looped to 'Y' default 'N'
例如:
with org_chart (employee_id, first_name, last_name, manager_id, department_id
) as (select employee_id, first_name, last_name, manager_id, department_idfrom employeeswhere employee_id = 100union allselect e.employee_id, e.first_name, e.last_name, e.manager_id, e.department_id from org_chart ocjoin employees eon e.manager_id = oc.employee_id
) cycle employee_id set looped to 'Y' default 'N'select * from org_chart;EMPLOYEE_ID FIRST_NAME LAST_NAME MANAGER_ID DEPARTMENT_ID LOOPED
______________ ______________ ____________ _____________ ________________ _________100 Steven King 107 90 N101 Neena Kochhar 100 90 N102 Lex De Haan 100 90 N108 Nancy Greenberg 101 100 N103 Alexander Hunold 102 60 N109 Daniel Faviet 108 100 N110 John Chen 108 100 N111 Ismael Sciarra 108 100 N112 Jose Manuel Urman 108 100 N113 Luis Popp 108 100 N104 Bruce Ernst 103 60 N105 David Austin 103 60 N106 Valli Pataballa 103 60 N107 Diana Lorentz 103 60 N100 Steven King 107 90 Y15 rows selected.
和Connect By不同,重复访问的行会显示在结果中,除非你过滤掉它。
另一个例子,是检测department_id是否有loop:
with org_chart (employee_id, first_name, last_name, manager_id, department_id
) as (select employee_id, first_name, last_name, manager_id, department_idfrom employeeswhere employee_id = 100union allselect e.employee_id, e.first_name, e.last_name, e.manager_id, e.department_id from org_chart ocjoin employees eon e.manager_id = oc.employee_id
) cycle department_id set looped to 'Y' default 'N'select * from org_chart;EMPLOYEE_ID FIRST_NAME LAST_NAME MANAGER_ID DEPARTMENT_ID LOOPED
______________ _____________ ____________ _____________ ________________ _________100 Steven King 107 90 N101 Neena Kochhar 100 90 Y102 Lex De Haan 100 90 Y
Displaying Tree Details: Connect By
使用level可以查看当前行在树中的深度。但要了解行之间的关系仍然很棘手。“Connect By”有许多选项可以帮助您实现这一点。
- Connect_by_root :从根行返回列的值
- Sys_connect_by_path:返回根和当前行之间所有行的值
- Connect_by_isleaf:判断行是否为叶节点。如果当前行是叶,则返回1。否则返回0
结合这些功能,您可以显示:
- 在每行显示CEO的姓氏(根行)
- 以逗号分隔列表显示从当前员工到CEO的管理链
- 不是经理的员工(叶子行)
SQL如下:
select employee_id, first_name, last_name, manager_id,connect_by_root last_name,sys_connect_by_path ( last_name, ', ') chart,connect_by_isleaf is_leaf
from employees
start with manager_id is null
connect by prior employee_id = manager_id;EMPLOYEE_ID FIRST_NAME LAST_NAME MANAGER_ID CONNECT_BY_ROOTLAST_NAME CHART IS_LEAF
______________ ______________ ____________ _____________ ___________________________ ______________________________________ __________100 Steven King King , King 0101 Neena Kochhar 100 King , King, Kochhar 0108 Nancy Greenberg 101 King , King, Kochhar, Greenberg 0109 Daniel Faviet 108 King , King, Kochhar, Greenberg, Faviet 1110 John Chen 108 King , King, Kochhar, Greenberg, Chen 1111 Ismael Sciarra 108 King , King, Kochhar, Greenberg, Sciarra 1112 Jose Manuel Urman 108 King , King, Kochhar, Greenberg, Urman 1113 Luis Popp 108 King , King, Kochhar, Greenberg, Popp 1102 Lex De Haan 100 King , King, De Haan 0103 Alexander Hunold 102 King , King, De Haan, Hunold 0104 Bruce Ernst 103 King , King, De Haan, Hunold, Ernst 1105 David Austin 103 King , King, De Haan, Hunold, Austin 1106 Valli Pataballa 103 King , King, De Haan, Hunold, Pataballa 1107 Diana Lorentz 103 King , King, De Haan, Hunold, Lorentz 114 rows selected.
Displaying Tree Details: Recursive With
Recursive with没有像connect by这样的内置选项,但你可以模仿:
with org_chart (employee_id, first_name, last_name, manager_id, root_emp, chart, lvl
) as (select employee_id, first_name, last_name, manager_id, last_name root_emp, last_name chart, 1 lvlfrom employeeswhere manager_id is nullunion allselect e.employee_id, e.first_name, e.last_name, e.manager_id, oc.root_emp, oc.chart || ', ' || e.last_name, oc.lvl+1from org_chart ocjoin employees eon e.manager_id = oc.employee_id
) search depth first by employee_id set seqselect oc.*, case when lead ( lvl, 1, 1 ) over ( order by seq ) <= lvl then 'LEAF'end is_leaffrom org_chart oc;EMPLOYEE_ID FIRST_NAME LAST_NAME MANAGER_ID ROOT_EMP CHART LVL SEQ IS_LEAF
______________ ______________ ____________ _____________ ___________ ____________________________________ ______ ______ __________100 Steven King King King 1 1101 Neena Kochhar 100 King King, Kochhar 2 2108 Nancy Greenberg 101 King King, Kochhar, Greenberg 3 3109 Daniel Faviet 108 King King, Kochhar, Greenberg, Faviet 4 4 LEAF110 John Chen 108 King King, Kochhar, Greenberg, Chen 4 5 LEAF111 Ismael Sciarra 108 King King, Kochhar, Greenberg, Sciarra 4 6 LEAF112 Jose Manuel Urman 108 King King, Kochhar, Greenberg, Urman 4 7 LEAF113 Luis Popp 108 King King, Kochhar, Greenberg, Popp 4 8 LEAF102 Lex De Haan 100 King King, De Haan 2 9103 Alexander Hunold 102 King King, De Haan, Hunold 3 10104 Bruce Ernst 103 King King, De Haan, Hunold, Ernst 4 11 LEAF105 David Austin 103 King King, De Haan, Hunold, Austin 4 12 LEAF106 Valli Pataballa 103 King King, De Haan, Hunold, Pataballa 4 13 LEAF107 Diana Lorentz 103 King King, De Haan, Hunold, Lorentz 4 14 LEAF14 rows selected.
环境清理
注意,此employees表源自HR实例Schema,所以不要删HR Schema下面的:
drop table employees;
Oracle开发者中级第7课(层级查询)实验相关推荐
- 浅谈oracle树状结构层级查询
oracle树状结构查询即层次递归查询,是sql语句经常用到的,在实际开发中组织结构实现及其层次化实现功能也是经常遇到的,虽然我是一个java程序开发者,我一直觉得只要精通数据库那么对于java开发你 ...
- 浅谈oracle树状结构层级查询测试数据
浅谈oracle树状结构层级查询 oracle树状结构查询即层次递归查询,是sql语句经常用到的,在实际开发中组织结构实现及其层次化实现功能也是经常遇到的,虽然我是一个java程序开发者,我一直觉得只 ...
- CUBRID学习笔记 42 Hierarchical QuerySQL层级查询
cubrid的中sql查询语法Hierarchical QuerySQL层级查询 ------ 官方文档是英文的,看不明白可以参看ocracle的同类函数说明.很多都是一样的. ORACLE中CONN ...
- Oracle(11g)数据库教程之十:Oracle操作题 (复习课)
Oracle(11g)数据库教程之十:Oracle操作题 (复习课) 操作题 Sutdent表的定义 字段名 字段描述 数据类型 主键 非空 Id 学号 INT(10) 是 是 Name 姓名 VAR ...
- 分析最近的一个网课答案查询2.0
分析最近的一个网课答案查询2.0 Lan 2020-05-06 18:50 1089 人阅读 0 条评论 这是一次没啥技术含量的分析,只为水一篇博文嘿嘿,这个接口也是这个作者免费开放的. ...
- oracle 11g circuits 参数,递归:在 Oracle Database 11g 第 2 版中查询层次结构数据
开发人员:SQL 递归: 在 Oracle Database 11g 第 2 版中查询层次结构数据的快速入门 作者:Tim Hartley 2010 年 2 月发表 递归数据库处理,也称为材料清单 或 ...
- 【DB笔试面试615】在Oracle中,和谓词相关的查询转换有哪些?
♣题目部分 在Oracle中,和谓词相关的查询转换有哪些? ♣答案部分 (一)过滤谓词推入 1LHR@orclasm > SELECT * FROM (SELECT * FROM VW_SVM_ ...
- 济南计算机中级职称查询,济南2018年中级会计考试成绩你查询了吗?
济南2018年中级会计师成绩查询入口已开通,囊括济南历下区.市中区.槐荫区.天桥区.历城区.长清区.平阴县.济阳县.商河县.章丘市考生,2018年中级会计职称考试成绩查分入口为全国会计资格评价网(ht ...
- 【DB笔试面试614】在Oracle中,和视图相关的查询转换有哪些?
♣题目 部分 在Oracle中,和视图相关的查询转换有哪些? ♣答案部分 (一)简单视图合并 1CREATE OR REPLACE VIEW VW_SVM_LHR AS SELECT * FROM S ...
最新文章
- Django搭建简单的站点
- FD.io/VPP — ACL
- spring aop代码的增强
- pycharm如何改为中文显示?
- book mac pro怎么重装系统_MAC笔记本电脑解决NTFS硬盘无法写入的简要方法
- Maven下载Sql Server 2008的驱动包
- 全套学习!mysql2003错误代码
- 猜数游戏c语言编程_做游戏,学编程(C语言) 10 僵尸危机
- unordered_set/unordered_map 增删查操作
- ACL与磁盘配额结合应用小结
- 电脑桌面上怎么找计算机,xp桌面上我的电脑图标不见了怎么找回来
- NIVIDIA 硬解码学习3
- python中endswith函数什么意思_Python中endswith()函数的基本使用
- 第13天 缓冲、转换、对象(序列化)和打印流
- 华为服务器故障灯不开机_华为服务器
- 「镁客早报」屠呦呦入选“20世纪最伟大人物”;四部门联合治理APP违法收集使用个人信息... 1
- oo 浏览文件服务器,文件服务器的配置.docx
- 银行排队叫号系统的模拟
- 《人人都是项目经理》之立项与启动(二)
- eclipse 中git解决冲突