'Oracle Hierarchical Query at depth level
I have a requirement to build a table from a hierarchical table. Table structure as below:
emp_hier table:
| emp_id | supervisorId |
|---|---|
| 100 | null |
| 1 | 100 |
| 2 | 1 |
| 3 | 2 |
New table:
I have to write a select query on the emp_heir table and the selected data should look like this:
| sel_emp_id | rel_emp_id | relation | depth_lvl |
|---|---|---|---|
| 100 | 100 | self | 0 |
| 100 | 1 | My Repotee | -1 |
| 100 | 2 | My Repotee | -2 |
| 100 | 3 | My Repotee | -3 |
| 1 | 100 | My Mgr | 1 |
| 1 | 1 | self | 0 |
| 1 | 2 | My Repotee | -1 |
| 1 | 3 | My Repotee | -2 |
| 2 | 1 | My Mgr | 1 |
| 2 | 2 | self | 0 |
| 2 | 3 | My Repotee | -1 |
| 3 | 100 | My Mgr | 3 |
| 3 | 1 | My Mgr | 2 |
| 3 | 2 | My Mgr | 1 |
| 3 | 3 | self | 0 |
Solution 1:[1]
Using CONNECT BY, you can connect all the employees and their relationships to each other. Then by joining that information together, you can print out the information in the format you desire.
WITH
hier
AS
( SELECT e.*, LEVEL AS lvl
FROM emp_hier e
CONNECT BY PRIOR emp_id = supervisorid
START WITH supervisorid IS NULL)
SELECT h1.emp_id AS sel_emp_id,
h2.emp_id AS rel_emp_id,
CASE
WHEN h1.lvl - h2.lvl = 0 THEN 'self'
WHEN h1.lvl - h2.lvl > 0 THEN 'My Mgr'
ELSE 'My Reportee'
END AS relation,
h1.lvl - h2.lvl AS depth_level
FROM hier h1, hier h2
ORDER BY CASE WHEN h1.supervisorid IS NULL THEN 0 ELSE 1 END, h1.emp_id, h1.lvl - h2.lvl DESC;
SEL_EMP_ID REL_EMP_ID RELATION DEPTH_LEVEL
_____________ _____________ ______________ ______________
100 100 self 0
100 1 My Reportee -1
100 2 My Reportee -2
100 3 My Reportee -3
1 100 My Mgr 1
1 1 self 0
1 2 My Reportee -1
1 3 My Reportee -2
2 100 My Mgr 2
2 1 My Mgr 1
2 2 self 0
2 3 My Reportee -1
3 100 My Mgr 3
3 1 My Mgr 2
3 2 My Mgr 1
3 3 self 0
Solution 2:[2]
You can get the entire desired result with a single pass through the hierarchy (a single CONNECT BY query), with no self-join and no union all.
Instead, I use a helper inline view (with just one row and two numeric columns, with the values -1 and 1); since each "relationship" appears exactly twice in the output, with the exception of "Self", I use this to do an ad-hoc duplication of the rows from the hierarchical query.
I used the table in MT0's post for testing. I don't show the result - it's the same (just ordered differently).
with
h (x) as (select 1 from dual union all select -1 from dual)
, p (ancestor, emp, depth) as (
select connect_by_root(emp_id), emp_id, level - 1
from emp_hier
connect by supervisorid = prior emp_id
)
select case h.x when 1 then emp else ancestor end as emp_self,
case h.x when 1 then ancestor else emp end as emp_related,
case when h.x = 1 then 'Mgr'
when p.depth != 0 then 'Reportee'
else 'Self' end as rel,
h.x * p.depth as depth_level
from p join h on h.x = -1 or p.depth != 0 -- do not duplicate "Self"
order by emp_self, depth_level desc, emp_related -- or whatever (if/as needed)
;
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | EJ Egyed |
| Solution 2 | mathguy |
