ORACLE层次查询技巧

ORACLE层次查询技巧

 

create table emp_hire as

selectempno,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_hiree

   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 asubquery, you cannot use LEVEL on the left-hand sideof the condition. However, you can specify LEVEL in a subquery of the FROM clause to achieve the same result. For example, thefollowing statement is not valid:

 

SELECT employee_id, last_name FROM employees

WHERE (employee_id,LEVEL)

IN (SELECTemployee_id, 2 FROM employees)

START WITHemployee_id = 2

CONNECT BY PRIORemployee_id = manager_id;

But the following statement is valid because it encapsulates the querycontaining 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 WITHemployee_id = 100

CONNECT BY PRIORemployee_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 <= 3AND department_id = 80

START WITH last_name= 'King'

CONNECT BY NOCYCLEPRIOR 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_hireset 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_hiree

   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 orv_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 intoc_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));

ifv_pos>0 andc_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 intoc_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 intoc_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));

ifv_pos>0   then

ifc_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 planfor

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 hashvalue: 2148023842

 

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

| Id  | Operation                               | Name     | Rows | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECTSTATEMENT                        |          |   15 |   495 |    3   (0)| 00:00:01 |

|*  1 |  CONNECT BY NOFILTERING WITH START-WITH|         |       |       |            |          |

|   2 |   TABLE ACCESS FULL                     | EMP_HIRE |    15 |  495 |     3   (0)| 00:00:01|

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

 

Predicate Information (identified byoperation 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,c,String,table,Integer,Path)