简体   繁体   中英

Oracle - using connect by prior in Hierarchical Queries and put them into rows

I am new in Oracle, I am going to use connect by prior to implement the flat table instead of Hierarchical one. but I am a little bit confuse. My table is like this: empTabl:

empID empName managerID
100 Sara 110
101 Ben 111
102 Alex 110
110 Ross 111
111 Mon NULL

I am going to change the table like this(output):

emp empName subBoss subBossName Boss BossName
100 Sara 110 Ross 111 Mon
101 Ben 111 Mon NULL NULL
102 Alex 110 Ross 111 Mon
110 Ross 111 Mon NULL NULL
111 Mon NULL NULL NULL NULL

You can use a recursive sub-query factoring clause for this:

WITH hierarchy (empID, empName, subBoss, subBossName, boss, bossName, depth, managerId) AS (
  SELECT empID,
         empName,
         CAST(NULL AS NUMBER),
         CAST(NULL AS VARCHAR2(20)),
         CAST(NULL AS NUMBER),
         CAST(NULL AS VARCHAR2(20)),
         1,
         managerID
  FROM   empTbl
UNION ALL
  SELECT h.empID,
         h.empName,
         CASE h.depth
         WHEN 1 THEN e.empID
         ELSE h.subBoss
         END,
         CASE h.depth
         WHEN 1 THEN e.empName
         ELSE h.subBossName
         END,
         CASE h.depth
         WHEN 2 THEN e.empID
         ELSE h.boss
         END,
         CASE h.depth
         WHEN 2 THEN e.empName
         ELSE h.bossName
         END,
         h.depth + 1,
         e.managerID
  FROM   hierarchy h
         LEFT OUTER JOIN empTbl e
         ON (h.managerID = e.empID)
  WHERE  depth < 3
)
CYCLE empID, depth SET is_cycle TO 1 DEFAULT 0
SELECT empID, empName, subBoss, subBossName, boss, bossName
FROM   hierarchy
WHERE  depth = 3;

Or, you can use a hierarchical query and pivot:

SELECT emp_id AS empID,
       emp_name AS empName,
       subboss_id AS subbossid,
       subboss_name AS subbossname,
       boss_id AS bossid,
       boss_name AS bossname
FROM   (
  SELECT CONNECT_BY_ROOT(empID) AS root_empid,
         empID,
         empName,
         LEVEL AS depth
  FROM   empTbl
  WHERE  LEVEL <= 3
  CONNECT BY PRIOR managerID = empID
)
PIVOT (
  MAX(empID) AS id,
  MAX(empName) AS name
  FOR depth IN (
    1 AS emp,
    2 AS subBoss,
    3 AS boss
  )
)
ORDER BY empid;

Which, for the sample data:

CREATE TABLE empTbl (empID, empName, managerID) AS
SELECT 100, 'Sara', 110 FROM DUAL UNION ALL
SELECT 101, 'Ben',  111 FROM DUAL UNION ALL
SELECT 102, 'Alex', 110 FROM DUAL UNION ALL
SELECT 110, 'Ross', 111 FROM DUAL UNION ALL
SELECT 111, 'Mon',  NULL FROM DUAL;

Both output:

EMPID EMPNAME SUBBOSS SUBBOSSNAME BOSS BOSSNAME
100 Sara 110 Ross 111 Mon
102 Alex 110 Ross 111 Mon
101 Ben 111 Mon
110 Ross 111 Mon
111 Mon

db<>fiddle here

You can also use connect by clause for that purpose.

select empID, empName
  , subBoss, subBossName
  , (select boss.empID from YourTable boss where boss.empID = subBossManagerID) Boss
  , (select boss.empName from YourTable boss where boss.empID = subBossManagerID) BossName
from (
  select empID, empName
    , prior empID subBoss, prior empName subBossName
    , prior t.managerID subBossManagerID
  from YourTable t
  start with managerID is null
  connect by prior empID = managerID
)
order by 1
;

test here

The shorter version of MDO's answer would be -

WITH DATA AS (SELECT 100 AS empID, 'Sara' AS empName, 110 AS managerID FROM DUAL UNION ALL
              SELECT 101, 'Ben', 111 FROM DUAL UNION ALL
              SELECT 102, 'Alex', 110 FROM DUAL UNION ALL
              SELECT 110, 'Ross', 111 FROM DUAL UNION ALL
              SELECT 111, 'Mon', NULL FROM DUAL)
 SELECT empID emp, empName, prior empID subBoss, prior empName subBossName
    , prior t.managerID Boss
    ,CASE WHEN PRIOR t.managerID IS NOT NULL THEN CONNECT_BY_ROOT(t.empName) END AS BossName
  FROM DATA T
 START WITH managerID IS NULL
CONNECT BY PRIOR empID = managerID
  ORDER BY 1;

Demo.

Ankit Bajpai's answer gives wrong name for manager after level 4 because of connect by root usage. With small notification, this query gives shortest answer

WITH DATA AS (SELECT 100 AS empID, 'Sara' AS empName, 110 AS managerID FROM DUAL UNION ALL
              SELECT 101, 'Ben', 111 FROM DUAL UNION ALL
              SELECT 102, 'Alex', 110 FROM DUAL UNION ALL
              SELECT 110, 'Ross', 111 FROM DUAL UNION ALL
              SELECT 111, 'Mon', NULL FROM DUAL)
, b as( SELECT p.empID , p.empName, p.managerID, m.empName as managerName  FROM DATA p left join data m
   on ( p.managerID=m.empID))
 SELECT empID emp, empName, prior empID subBoss, prior empName subBossName
    , prior managerID Boss, prior managerName BossNAme
  FROM b
 START WITH managerID IS NULL
CONNECT BY PRIOR empID = managerID
  ORDER BY 1;

fiddle

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