Hierarchy formatting

60 views Asked by At

I am trying to create a query that shows the different levels of the hierarchy in different columns For now it looks like this

Parent Child
1 2
1 3
2 4
2 5
3 5

I want the output to look like this

Root Child1 Child2 Child3 Child4
1 2 4
1 2 5
1 3 5
2

There are 2 answers

1
MT0 On

You can use a recursive query:

WITH hierarchy (root, child, child1, child2, child3, child4, child5, lvl) AS (
  SELECT parent,
         child,
         child,
         CAST(NULL AS NUMBER),
         CAST(NULL AS NUMBER),
         CAST(NULL AS NUMBER),
         CAST(NULL AS NUMBER),
         1
  FROM   table_name
  WHERE  parent = 1
UNION ALL
  SELECT h.root,
         t.child,
         h.child1,
         CASE lvl + 1 WHEN 2 THEN t.child ELSE h.child2 END,
         CASE lvl + 1 WHEN 3 THEN t.child ELSE h.child3 END,
         CASE lvl + 1 WHEN 4 THEN t.child ELSE h.child4 END,
         CASE lvl + 1 WHEN 5 THEN t.child ELSE h.child5 END,
         lvl + 1
  FROM   hierarchy h
         LEFT OUTER JOIN table_name t
         ON (h.child = t.parent)
  WHERE  lvl < 5
)
CYCLE root, child1, child2, child3, child4, child5, lvl SET is_cycle TO 1 DEFAULT 0
SELECT root,
       child1,
       child2,
       child3,
       child4,
       child5
FROM   hierarchy
WHERE  lvl = 5

or:

SELECT REGEXP_SUBSTR(path, '[^|]+', 1, 1) AS root,
       REGEXP_SUBSTR(path, '[^|]+', 1, 2) AS child1,
       REGEXP_SUBSTR(path, '[^|]+', 1, 3) AS child2,
       REGEXP_SUBSTR(path, '[^|]+', 1, 4) AS child3,
       REGEXP_SUBSTR(path, '[^|]+', 1, 5) AS child4,
       REGEXP_SUBSTR(path, '[^|]+', 1, 6) AS child5
FROM   (
  SELECT CONNECT_BY_ROOT parent || SYS_CONNECT_BY_PATH(child, '|') AS path
  FROM   table_name
  WHERE  CONNECT_BY_ISLEAF = 1
  START WITH parent = 1
  CONNECT BY PRIOR child = parent
)

Which, for the sample data:

CREATE TABLE table_name (Parent, Child) AS
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 1, 3 FROM DUAL UNION ALL
SELECT 2, 4 FROM DUAL UNION ALL
SELECT 2, 5 FROM DUAL UNION ALL
SELECT 3, 5 FROM DUAL UNION ALL
SELECT 4, 6 FROM DUAL UNION ALL
SELECT 6, 7 FROM DUAL UNION ALL
SELECT 7, 8 FROM DUAL;

Both output:

ROOT CHILD1 CHILD2 CHILD3 CHILD4 CHILD5
1 2 4 6 7 8
1 2 5 null null null
1 3 5 null null null

db<>fiddle here

0
Thorsten Kettner On

A simple self-join does the job:

select
  c1.parent as root,
  c1.child as child1,
  c2.child as child2,
  c3.child as child3,
  c4.child as child4
from mytable c1
left join mytable c2 on c2.parent = c1.child
left join mytable c3 on c3.parent = c2.child
left join mytable c4 on c4.parent = c3.child
where c1.parent not in (select child from mytable)
order by root, child1, child2, child3, child4;

Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=fcc7963188b28b519417142a4b5f70f6