`

层次查询

阅读更多
层次查询

create table emp_hire as
select empno,mgr,ename from emp

select * from emp_hire
EMPNO	MGR	ENAME
5555	7900	ggg
7369	7902	SMITH
7499	7698	ALLEN
7521	7698	WARD
7566	7839	JONES
7654	7698	MA & RTIN
7698	7839	BLAKE
7782	7839	CLARK
7788	7566	SCOTT
7839	 	KING
7844	7698	TURNER
7900	7698	JAMES
7902	7566	FORD
7934	7782	MILLER
7876	7788	ADAMS

这些数据存在着层次关系
select 
   lpad('*',level,'*')||e.ename ename, 
   e.empno, 
   mgr fa_id, 
   (select ename from emp_hire where empno=e.mgr) fa_name
from emp_hire e
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR

ENAME	EMPNO	FA_ID	FA_NAME
*KING	7839	 	 
**JONES	7566	7839	KING
***SCOTT	7788	7566	JONES
****ADAMS	7876	7788	SCOTT
***FORD	7902	7566	JONES
****SMITH	7369	7902	FORD
**BLAKE	7698	7839	KING
***ALLEN	7499	7698	BLAKE
***WARD	7521	7698	BLAKE
***MA & RTIN	7654	7698	BLAKE
***TURNER	7844	7698	BLAKE
***JAMES	7900	7698	BLAKE
****ggg	5555	7900	JAMES
**CLARK	7782	7839	KING
***MILLER	7934	7782	CLARK



其中
start with -- this identifies all LEVEL=1 nodes in the tree

connect by -- describes how to walk from the parent nodes above to their children and their childrens children.

层次是一棵树, 又如同一个家族图谱
 

每一个子节电只有一个父节点;
每一个分支,最末端是叶节点;

SELECT 
ename  Employee, 
CONNECT_BY_ISLEAF  IsLeaf,
LEVEL lev, 
SYS_CONNECT_BY_PATH(ename, '/')  Path
FROM emp
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR

EMPLOYEE	ISLEAF	LEV	PATH
KING	0	1	/KING
JONES	0	2	/KING/JONES
SCOTT	0	3	/KING/JONES/SCOTT
ADAMS	1	4	/KING/JONES/SCOTT/ADAMS
FORD	0	3	/KING/JONES/FORD
SMITH	1	4	/KING/JONES/FORD/SMITH
BLAKE	0	2	/KING/BLAKE
ALLEN	1	3	/KING/BLAKE/ALLEN
WARD	1	3	/KING/BLAKE/WARD
MA & RTIN	1	3	/KING/BLAKE/MA & RTIN
TURNER	1	3	/KING/BLAKE/TURNER
JAMES	0	3	/KING/BLAKE/JAMES
ggg	1	4	/KING/BLAKE/JAMES/ggg
CLARK	0	2	/KING/CLARK
MILLER	1	3	/KING/CLARK/MILLER



检查一个层次是否存在闭循环
CONNECT_BY_ISCYCLE

找出根节点
CONNECT_BY_ROOT

SELECT 
ename  Employee, 
CONNECT_BY_ISCYCLE,
CONNECT_BY_ROOT ename 
FROM emp
    START WITH MGR IS NULL
    CONNECT BY NOCYCLE PRIOR EMPNO = MGR
EMPLOYEE	CONNECT_BY_ISCYCLE	CONNECT_BY_ROOTENAME
KING	0	KING
JONES	0	KING
SCOTT	0	KING
ADAMS	0	KING
FORD	0	KING
SMITH	0	KING
BLAKE	0	KING
ALLEN	0	KING
WARD	0	KING
MA & RTIN	0	KING
TURNER	0	KING
JAMES	0	KING
ggg	0	KING
CLARK	0	KING
MILLER	0	KING


层次查询的过滤条件
SELECT 
ename  Employee, 
CONNECT_BY_ISLEAF  IsLeaf,
LEVEL lev, 
SYS_CONNECT_BY_PATH(ename, '/')  Path
FROM emp
where level<3
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR

Restriction on LEVEL in WHERE Clauses In a [NOT] IN condition in a WHERE clause, if the right-hand side of the condition is a subquery, you cannot use LEVEL on the left-hand side of the condition. However, you can specify LEVEL in a subquery of the FROM clause to achieve the same result. For example, the following statement is not valid:

SELECT employee_id, last_name FROM employees
WHERE (employee_id, LEVEL)
IN (SELECT employee_id, 2 FROM employees)
START WITH employee_id = 2
CONNECT BY PRIOR employee_id = manager_id;
But the following statement is valid because it encapsulates the query containing the
LEVEL information in the FROM clause:

SELECT v.employee_id, v.last_name, v.lev
FROM
(SELECT employee_id, last_name, LEVEL lev
FROM employees v
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id) v
WHERE (v.employee_id, v.lev) IN
(SELECT employee_id, 2 FROM employees);



SIBLINGS的排序

SELECT 
ename  Employee, 
CONNECT_BY_ISLEAF  IsLeaf,
LEVEL lev, 
SYS_CONNECT_BY_PATH(ename, '/')  Path
FROM emp
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR
ORDER SIBLINGS BY ename desc
EMPLOYEE	ISLEAF	LEV	PATH
KING	0	1	/KING
JONES	0	2	/KING/JONES
SCOTT	0	3	/KING/JONES/SCOTT
ADAMS	1	4	/KING/JONES/SCOTT/ADAMS
FORD	0	3	/KING/JONES/FORD
SMITH	1	4	/KING/JONES/FORD/SMITH
CLARK	0	2	/KING/CLARK
MILLER	1	3	/KING/CLARK/MILLER
BLAKE	0	2	/KING/BLAKE
WARD	1	3	/KING/BLAKE/WARD
TURNER	1	3	/KING/BLAKE/TURNER
MA & RTIN	1	3	/KING/BLAKE/MA & RTIN
JAMES	0	3	/KING/BLAKE/JAMES
ggg	1	4	/KING/BLAKE/JAMES/ggg
ALLEN	1	3	/KING/BLAKE/ALLEN

注意:ORDER SIBLINGS By一定与start with 与 connect by一起使用



检查树是否有闭循环
SELECT last_name "Employee", CONNECT_BY_ISCYCLE "Cycle",
LEVEL, SYS_CONNECT_BY_PATH(last_name, '/') "Path"
FROM employees
WHERE level <= 3 AND department_id = 80
START WITH last_name = 'King'
CONNECT BY NOCYCLE PRIOR employee_id = manager_id AND LEVEL <= 4;

Employee	Cycle	LEVEL	Path
King	0	1	/King
Russell	0	2	/King/Russell
Tucker	0	3	/King/Russell/Tucker
Bernstein	0	3	/King/Russell/Bernstein
Hall	0	3	/King/Russell/Hall
Olsen	0	3	/King/Russell/Olsen
Cambrault	0	3	/King/Russell/Cambrault
Tuvault	0	3	/King/Russell/Tuvault
Partners	0	2	/King/Partners
King	0	3	/King/Partners/King



层次查询的累计运算

将非叶节点的度量sal置为空
update emp_hire set sal=null
where empno in
(
select empno from
(
SELECT 
empno, 
CONNECT_BY_ISLEAF  IsLeaf,
LEVEL lev, 
SYS_CONNECT_BY_PATH(ename, '/')  Path
FROM emp
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR
)
where isleaf=0
)

察看数据

SELECT 
empno, 
CONNECT_BY_ISLEAF  IsLeaf,
LEVEL lev, 
SYS_CONNECT_BY_PATH(ename, '/')  Path,
sal
FROM emp_hire
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR

EMPNO	ISLEAF	LEV	PATH	SAL
7839	0	1	/KING	 
7566	0	2	/KING/JONES	 
7788	0	3	/KING/JONES/SCOTT	 
7876	1	4	/KING/JONES/SCOTT/ADAMS	1100.00
7902	0	3	/KING/JONES/FORD	 
7369	1	4	/KING/JONES/FORD/SMITH	800.00
7698	0	2	/KING/BLAKE	 
7499	1	3	/KING/BLAKE/ALLEN	1600.00
7521	1	3	/KING/BLAKE/WARD	1250.00
7654	1	3	/KING/BLAKE/MA & RTIN	1250.00
7844	1	3	/KING/BLAKE/TURNER	1500.00
7900	0	3	/KING/BLAKE/JAMES	 
5555	1	4	/KING/BLAKE/JAMES/ggg	0.00
7782	0	2	/KING/CLARK	 
7934	1	3	/KING/CLARK/MILLER	1300.00

select 
sum(sal),
 sum(power(0.5,(level-1))*sal) 
from emp_hire
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR

SUM(SAL)	SUM(POWER(0.5,(LEVEL-1))*SAL)
8800	1962.5


我们不能用以下的方法计算累计值
select 
SYS_CONNECT_BY_PATH(ename, '/')  Path, sum(sal),
 sum(power(0.5,(level-1))*sal) 
from emp_hire
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR
group by Path

我们可以找出一个节点的所有子孙节点,以及这些节点所在的level

select 
empno,   
SYS_CONNECT_BY_PATH(ename, '/')  Path, sal,
--,sum(power(0.5,(level-1))*sal) 
(
select sum(power(0.5,(level-1))*nvl(sal,0)) from emp_hire
 start with empno=e.empno
 connect by prior empno= mgr
) s_sal,
(
select sum(nvl(sal,0)) from emp_hire
 start with empno=e.empno
 connect by prior empno= mgr
) s1_sal
from emp_hire e
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR
EMPNO	PATH	SAL	S_SAL	S1_SAL
7839	/KING	 	1962.5	8800
7566	/KING/JONES	 	475	1900
7788	/KING/JONES/SCOTT	 	550	1100
7876	/KING/JONES/SCOTT/ADAMS	1100.00	1100	1100
7902	/KING/JONES/FORD	 	400	800
7369	/KING/JONES/FORD/SMITH	800.00	800	800
7698	/KING/BLAKE	 	2800	5600
7499	/KING/BLAKE/ALLEN	1600.00	1600	1600
7521	/KING/BLAKE/WARD	1250.00	1250	1250
7654	/KING/BLAKE/MA & RTIN	1250.00	1250	1250
7844	/KING/BLAKE/TURNER	1500.00	1500	1500
7900	/KING/BLAKE/JAMES	 	0	0
5555	/KING/BLAKE/JAMES/ggg	0.00	0	0
7782	/KING/CLARK	 	650	1300
7934	/KING/CLARK/MILLER	1300.00	1300	1300


也可以通过以下过程实现:

create type myTable is table of varchar2(100);

CREATE OR REPLACE FUNCTION str_to_table (
                 string_in    IN VARCHAR2 ,
                 delimiter_in IN VARCHAR2 DEFAULT ','
                 ) RETURN myTable IS
       v_wkg_str VARCHAR2 ( 32767 ) := string_in || delimiter_in;
       v_pos     PLS_INTEGER ;
       nt_return myTable := myTable();
       i_count  pls_integer;
    BEGIN
       LOOP
          v_pos := INSTR(v_wkg_str,delimiter_in);
          EXIT WHEN NVL(v_pos, 0 ) = 0  or v_wkg_str=delimiter_in;
          if TRIM (SUBSTR(v_wkg_str, 1 ,v_pos- 1 )) is not null  then
          nt_return. EXTEND ;
          nt_return(nt_return. LAST ) := TRIM (SUBSTR(v_wkg_str, 1 ,v_pos- 1 ));
          end if;
          v_wkg_str := SUBSTR(v_wkg_str,v_pos+ 1 );
       END LOOP ;
       RETURN nt_return;     
    END ;


declare 
--create or replace type v2 is table of varchar2(2000)
  c_path v2;
  c_level v1;
  c_empno v1;
  v_pos   integer;
  v_string  varchar2(2000);
begin
select 
empno,SYS_CONNECT_BY_PATH(empno, '/')  Path, level lev 
bulk collect into c_empno,c_path,c_level
from emp_hire
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR;
for i in 1..c_empno.count loop
v_string:='';
for j in 1..c_path.count loop

v_pos := INSTR(c_path(j),c_empno(i));
if v_pos>0 and c_empno(i)<>c_empno(j) then
v_string:=v_string||'/'||c_empno(j);

end if;
end loop; 
dbms_output.put_line(c_empno(i)||'   '||v_string);
end loop;      
end;

输出结果
7839   /7566/7788/7876/7902/7369/7698/7499/7521/7654/7844/7900/5555/7782/7934
7566   /7788/7876/7902/7369
7788   /7876
7876   
7902   /7369
7369   
7698   /7499/7521/7654/7844/7900/5555
7499   
7521   
7654   
7844   
7900   /5555
5555   
7782   /7934
7934

如果要计算累计结果则
declare 
--create or replace type v2 is table of varchar2(2000)
  c_path v2;
  c_level v1;
  c_empno v1;
  c_sal  v1;
  v_pos   integer;
  v_string  varchar2(2000);
  v_sal  number;
 
begin
select 
empno,SYS_CONNECT_BY_PATH(empno, '/')  Path, level lev, nvl(sal,0)
bulk collect into c_empno,c_path,c_level,c_sal
from emp_hire
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR;
for i in 1..c_empno.count loop
v_string:='';
v_sal:=0;
for j in 1..c_path.count loop

v_pos := INSTR(c_path(j),c_empno(i));
if v_pos>0   then
if c_empno(i)<>c_empno(j) then
v_string:=v_string||'/'||c_empno(j);
end if;
v_sal:=v_sal+c_sal(j);
end if;
end loop; 
dbms_output.put_line(c_empno(i)||'   '||v_string||'   '||v_sal);
end loop;      
end;

7839   8800   /7566/7788/7876/7902/7369/7698/7499/7521/7654/7844/7900/5555/7782/7934
7566   1900   /7788/7876/7902/7369
7788   1100   /7876
7876   1100   
7902   800   /7369
7369   800   
7698   5600   /7499/7521/7654/7844/7900/5555
7499   1600   
7521   1250   
7654   1250   
7844   1500   
7900   0   /5555
5555   0   
7782   1300   /7934
7934   1300


如果存在一定的累计规则
则
declare 
--create or replace type v2 is table of varchar2(2000)
  c_path v2;
  c_level v1;
  c_empno v1;
  c_sal  v1;
  v_pos   integer;
  v_string  varchar2(2000);
  v_sal  number;
 
begin
select 
empno,SYS_CONNECT_BY_PATH(empno, '/')  Path, level lev, nvl(sal,0)
bulk collect into c_empno,c_path,c_level,c_sal
from emp_hire
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR;
for i in 1..c_empno.count loop
v_string:='';
v_sal:=0;
for j in 1..c_path.count loop

v_pos := INSTR(c_path(j),c_empno(i));
if v_pos>0   then
if c_empno(i)<>c_empno(j) then
v_string:=v_string||'/'||c_empno(j);
end if;
v_sal:=v_sal+power(0.5,(c_level(j)-c_level(i)))*c_sal(j);
end if;
end loop; 
dbms_output.put_line(c_empno(i)||'   '||'    '||c_sal(i)||'    '||v_sal||'   '||v_string);
end loop;      
end;

7839       0    1962.5   /7566/7788/7876/7902/7369/7698/7499/7521/7654/7844/7900/5555/7782/7934
7566       0    475   /7788/7876/7902/7369
7788       0    550   /7876
7876       1100    1100   
7902       0    400   /7369
7369       800    800   
7698       0    2800   /7499/7521/7654/7844/7900/5555
7499       1600    1600   
7521       1250    1250   
7654       1250    1250   
7844       1500    1500   
7900       0    0   /5555
5555       0    0   
7782       0    650   /7934
7934       1300    1300  

-----------------------------------------------------------------------------------------------------------

SELECT 
empno,
ename  Employee, 
CONNECT_BY_ROOT ename,
level lev 
FROM (select * from emp_hire where ename<>'KING')
    START WITH MGR IS NULL
    CONNECT BY NOCYCLE PRIOR EMPNO = MGR


层次查询的效率

Explain plan for
select 
   lpad('*',level,'*')||e.ename ename, 
   e.empno, 
   mgr fa_id
from emp_hire e
    START WITH MGR IS NULL
    CONNECT BY PRIOR EMPNO = MGR
    
    
select * from table(dbms_xplan.display());


Plan hash value: 2148023842
 
----------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |          |    15 |   495 |     3   (0)| 00:00:01 |
|*  1 |  CONNECT BY NO FILTERING WITH START-WITH|          |       |       |            |          |
|   2 |   TABLE ACCESS FULL                     | EMP_HIRE |    15 |   495 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("MGR"=PRIOR "EMPNO")
       filter("MGR" IS NULL)
 
Note
-----
   - dynamic sampling used for this statement

层次查询从本质上来说是递归运算,因此对于大的层次查询,可以分解为多个层次查询,从而减少计算量。

层次查询的副产品
产生自然数的查询
select rownum r  from dual  connect by level <= 100



层次向普通对称维度表的转换



 

分享到:
评论

相关推荐

    【原创】oracle树形结构查询,层次查询,hierarical retrival

    Oracle树形结构查询,层次查询,hierarchical retrieval Oracle中的树形结构查询,也被称为层次查询或hierarchical retrieval,是一种获取树形结构数据的方法。这种方法可以将数据组织成树形结构,具有层次关系的...

    Oracle实战层次查询

    层次查询的概念 层次查询的语法 基本层次查询 层次查询的增强特性 CONNECT BY特殊用法示例

    c++英文材料词汇难度层次查询系统 源代码 原创

    《C++英文材料词汇难度层次查询系统》源代码解析与应用 C++是一种广泛应用的编程语言,尤其在系统软件、嵌入式系统、游戏开发等领域有着广泛的用途。本项目——"C++英文材料词汇难度层次查询系统",是基于C++实现的...

    Oracle 实战SQL层次查询

    ### Oracle 实战SQL层次查询详解 #### 一、层次查询概念 层次查询,也被称为树型结构查询,是在SQL中最常见的功能之一。这种查询方式主要用于处理具有层级结构的数据,例如组织架构、产品分类等场景。在Oracle...

    SqlServer树形结构、层次查询

    SqlServer树形结构、层次查询 结果如下: 1 1 2 1-2 4 1-2-4 5 1-2-5 10 1-2-5-10 8 1-2-5-8 6 1-2-6 3 1-3 7 1-3-7 11 1-3-7-11 9 1-3-7-9 12 1-3-7-9-12 13 1-3-7-9-13 14 1-3-7-9-14

    数据库开发 Oracle数据库 SQL开发教程 第10章 层次查询(共17页).pdf

    在Oracle数据库中,SQL开发是核心技能之一,尤其在处理复杂的数据关系时,层次查询(Hierarchical Queries)显得尤为重要。本教程的第10章专门介绍了这一主题,旨在帮助学员理解并掌握如何在数据中构建树型结构的...

    层次查询功能在Oracle数据库中的应用.pdf

    Oracle数据库是一种广泛使用的商业关系型数据库管理系统,其层次查询功能主要基于树形结构来处理层级数据,这种结构被广泛用于表示具有层级关系的数据,如人事组织结构、公司组织架构以及计算机目录结构等。层次查询...

    Oracle层次查询功能的剖析.pdf

    Oracle层次查询功能是Oracle数据库系统中的一个重要特性,它在关系型数据库管理中为处理具有层级关系的数据提供了便利。本文主要探讨了如何利用Oracle的层次查询功能处理和展示类似树形结构的数据。 首先,层次结构...

    Oracle中层次查询的使用和探讨.pdf

    Oracle数据库中的层次查询(Hierarchical Queries)是一种强大的功能,它扩展了标准SQL语言,使得能够高效地处理具有层级关系的数据。这种查询方式尤其适用于处理树形结构的数据,例如组织架构、文件目录等。 首先...

    内部编码和层次查询测试分析

    ### 内部编码与层次查询测试分析:深入探讨与比较 #### 1. 内部编码法:一种高效的树形结构处理方式 内部编码法,作为处理树形结构的一种手段,其核心在于通过预定义的编码规则来表示节点之间的层级关系。在本案例...

    sqlserver父子层次查询.pdf

    在SQL Server中,进行父子层次查询是数据库管理中常见的需求,尤其在组织结构、部门层级或者树状数据模型中。本文件"sqlserver父子层次查询.pdf"主要探讨了如何使用递归公共表表达式(Recursive Common Table ...

    oracle层次查询

    ### Oracle层次查询详解 在Oracle数据库中,处理具有层次结构的数据是一项常见的需求,尤其是在企业管理、财务分析、组织架构展示等领域。Oracle提供了强大的`CONNECT BY`子句,它能够有效地进行层次查询,帮助用户...

    多级数据-Mysql中的递归层次查询(父子查询).doc

    递归层次查询(父子查询)在 MySQL 中的实现 在 MySQL 中,实现递归层次查询(父子查询)是一种复杂的操作,特别是当我们需要查询某个节点下的所有节点或节点上的所有父节点时。 Oracle 中有 Hierarchical Queries ...

    第10章 层次查询

    通过本章学习,学员应达到如下目标: 解释层次查询的概念; 创建一个树型结构的报告; 格式化分级数据; 从树型结构中去除分支;

    在Oracle层次查询中给SIBLINGS排序

    Oracle SELECT语句中的START WITH和CONNECT BY子句自动阻断一个层次。缺少这项特性,就需要一个复杂的自联接来确定行之间的逻辑联系。START WITH子句指定被...本文将为大家介绍如何在Oracle层次查询中给SIBLINGS排序。

    oracle 层次查询

    高级sql关于层次查询更新 对于层次查询需要掌握: 1.解释层次查询的基本概念,识别需求中需要用到层次查询的能力。 2.建立和格式化一个树形报表(tree report)。 3.修剪树形结构的节点(node)和枝(branches)。

    Oracle层次查询和with函数的使用示例

    Oracle数据库在处理层次数据时,提供了强大的查询能力,其中就包括层次查询(Hierarchical Query)和WITH函数。这两种方法在处理具有层级结构的数据,如组织结构、产品分类、树状菜单等场景时尤为有用。 首先,我们...

    SQL 高级查询技术

    本章主要探讨三个核心主题:日期和时间处理、层次查询以及分析查询,这些都是Oracle Database 10g系统中重要的特性。 首先,日期和时间处理在分布式数据库环境中尤为重要,因为全球各地的时间差异可能会影响数据的...

    Oracle 11g教程(包,层次化查询,触发器)

    通过视频的方式详细叙述Oracle 11g中包,层次化查询和触发器的用法

Global site tag (gtag.js) - Google Analytics