简体   繁体   中英

Interaction of where clause with connect by And Creating query to fetch next level in a hierarchy

Table:

create table temp_hierarchy_define (dept varchar2(25), parent_dept varchar2(25))
create table temp_employee (empid number(1), empname varchar2(50), dept varchar2(25), salary number(10))

Data

Select 'COMPANY' dept   , 'COMPANY' parent_dept  From Dual Union All
Select 'IT'        , 'COMPANY'  From Dual Union All
Select 'MARKET'    , 'COMPANY'  From Dual Union All
Select 'ITSEC'     , 'IT'       From Dual Union All
Select 'ITDBA'     , 'IT'       From Dual Union All
Select 'ITDBAORC'  , 'ITDBA'    From Dual Union All
Select 'ITDBASQL'  , 'ITDBA'    From Dual

select 1 empid, 'Rohan-ITDBASQL' empname   ,'ITDBASQL' dept ,10 salary from dual union all
select 2, 'Raj-ITDBAORC'     ,'ITDBAORC'  ,20 from dual union all
select 3, 'Roy-ITDBA'        ,'ITDBA'     ,30 from dual union all
select 4, 'Ray-MARKET'       ,'MARKET'    ,40 from dual union all
select 5, 'Roopal-IT'        ,'IT'        ,50 from dual union all
select 6, 'Ramesh-ITSEC'     ,'ITSEC'     ,60 from dual 

Requirement

Summarize salary of all IT dept:

CATEGORY SALARY

5,50

ITSEC,60

ITDBA,60

Summarize salary of all COMPANY dept:

CATEGORY SALARY

IT,170

MARKET,40

Summarize salary of all ITDBA dept:

CATEGORY SALARY

3,30

ITDBASQL,10

ITDBAORC,20

You will notice that we are trying to summarize based on the next level in the hierarchy. If any emp is already part of that level then we need to show the employee.

Trial Query:

Select Category,sum(salary) from (
Select 
NVL((Select dept.dept from temp_hierarchy_define dept
               Where dept.parent_dept = 'IT'
               And dept.dept != 'IT' 
               Start With dept.dept = emp.dept
               Connect by NOCYCLE dept.dept = Prior dept.parent_dept
               and prior dept.dept is not null),emp.empid) category,
emp.*
From temp_employee emp
Where emp.DEPT in
(Select dept.dept from temp_hierarchy_define dept
Start With dept.dept = 'IT' 
connect by nocycle prior dept.dept = dept.parent_dept) ) Group by Category

Concerns & queries:

  1. Whether this query will work well in all scenarios. Or there any better way of doing it ??
  2. How does where condition interact with connect by. For eg in the sub query we are filtering with parent_dept = 'IT' , however while starting connect by some emp might have parent_dept = 'ITDBASQL' which is also part of IT. I am having a hard time in understanding the workflow.

Thank you for your time and assistance.

Or there any better way of doing it ?

This is an equivalent query that only requires one table scan for each table. You will need to determine whether your query or this one is more performant for your data/indexes/etc.

SQL Fiddle

Oracle 11g R2 Schema Setup :

create table temp_hierarchy_define (
  dept varchar2(25), parent_dept varchar2(25));
create table temp_employee (
  empid number(1), empname varchar2(50), dept varchar2(25), salary number(10));

INSERT INTO temp_hierarchy_define( dept, parent_dept )
Select 'COMPANY'   , 'COMPANY'  From Dual Union All
Select 'IT'        , 'COMPANY'  From Dual Union All
Select 'MARKET'    , 'COMPANY'  From Dual Union All
Select 'ITSEC'     , 'IT'       From Dual Union All
Select 'ITDBA'     , 'IT'       From Dual Union All
Select 'ITDBAORC'  , 'ITDBA'    From Dual Union All
Select 'ITDBASQL'  , 'ITDBA'    From Dual;

INSERT INTO temp_employee( empid, empname, dept, salary )
select 1, 'Rohan-ITDBASQL'   ,'ITDBASQL'  ,10 from dual union all
select 2, 'Raj-ITDBAORC'     ,'ITDBAORC'  ,20 from dual union all
select 3, 'Roy-ITDBA'        ,'ITDBA'     ,30 from dual union all
select 4, 'Ray-MARKET'       ,'MARKET'    ,40 from dual union all
select 5, 'Roopal-IT'        ,'IT'        ,50 from dual union all
select 6, 'Ramesh-ITSEC'     ,'ITSEC'     ,60 from dual;

Query 1 :

SELECT dept,
       SUM( salary )
FROM   (
  SELECT CASE
         WHEN lvl = 1 AND h.parent_dept = e.dept
         THEN CAST( e.empid AS VARCHAR2(25) )
         ELSE root_dept
         END AS dept,
         e.empid,
         e.salary
  FROM   ( SELECT CONNECT_BY_ROOT( dept ) AS root_dept,
                  h.*,
                  LEVEL AS lvl,
                  ROW_NUMBER() OVER ( PARTITION BY parent_dept ORDER BY ROWNUM ) AS rn
           FROM   temp_hierarchy_define h
           WHERE  parent_dept != dept
           START WITH h.parent_dept = 'IT'
           CONNECT BY NOCYCLE PRIOR h.dept = h.parent_dept
         ) h
         LEFT OUTER JOIN
         temp_employee e
         ON (  h.dept = e.dept
            OR ( h.parent_dept = e.dept AND h.lvl = 1 AND h.rn = 1)    
            )
)
GROUP BY dept

Results :

|  DEPT | SUM(SALARY) |
|-------|-------------|
| ITDBA |          60 |
|     5 |          50 |
| ITSEC |          60 |

You can run the queries individually to find out what they are doing:

SELECT CONNECT_BY_ROOT( dept ) AS root_dept,
       h.*,
       LEVEL AS lvl,
       ROW_NUMBER() OVER ( PARTITION BY parent_dept ORDER BY ROWNUM ) AS rn
FROM   temp_hierarchy_define h
WHERE  parent_dept != dept
START WITH h.parent_dept = 'IT'
CONNECT BY NOCYCLE PRIOR h.dept = h.parent_dept

Just lists all the rows in the hierarchy and uses CONNECT_BY_ROOT to get the department at the root of the branch of the hierarchy. LEVEL and ROW_NUMBER() are used to find the first row at the top of the hierarchy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM