0

I have a hierarchical table categories as follows:

id name parent_id
1 Accommodation null
2 Transport null
3 Utility 1
4 Maintenance 1
5 Private 2
6 Public 2
7 Electricity 3
8 Gas 3
9 Internet 3
10 Garden service 4
11 Repairs 4
12 Car repayment 5
13 .... ..

I want to transpose this to show one row for each leaf to be as follows (I know upfront the levels are 3 at max)

leaf_id leaf_name parent_id_1 parent_name_1 parent_id_2 parent_name_2
9 internet 3 Utility 1 Accommodation
8 Gas 3 Utility 1 Accommodation
12 Car repayment 5 Private 2 Transport
6 Public 2 Transport null null
.. .. .. .. .. .. ..

I tried with the following query, but I just could not get it right (for example, couldn't get the parent's name, only id:

SELECT * FROM
(
  SELECT id, name ,parent_id, level l
  FROM categories
connect by prior parent_id = id
)
PIVOT
(
  max(id)  --pivot clause
  FOR l   --pivot_for_clause
  IN (1 parent_id_1, 2 parent_id_2, 3 parent_id_2)  --pivot_in_clause
)
;
4
  • Can the levels be 2 or 1, and if yes, how would you represent them? Commented Nov 1, 2024 at 2:28
  • @tinazmu, sorry I did not get your question. Could explain? Commented Nov 1, 2024 at 2:30
  • Are all branches of your hierarchy consisting of exactly three nodes? GrandParent (Root)->Parent->Children (Leaf). You don't, for example, have a path, like, Other->Other Expenses(leaf), where Other has no parent.. (levels are exactly 3, instead being levels are 3 at max ) Commented Nov 1, 2024 at 2:49
  • @tinazmu, got you, there could be less than 3. It could be 1, or 2, or 3. I added last row in the output as an example for 2 Commented Nov 1, 2024 at 3:02

2 Answers 2

1

This should work in most RDBMSs (without using specific SQL extensions, or more advanced functions):

With LeafNodes as (
select *
from MyTbl LN
where not exists 
     (select 1
      from MyTbl PL
      where PL.parent_id=LN.id)
)
select  LN.*, P1.*,P2.*
from LeafNodes LN
     left join
     MyTbl P1
     on P1.id=LN.parent_id
     left join
     MyTbl P2
     on P2.id=P1.parent_id
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you. It worked for me. I posted another similar question with slight change in the output: stackoverflow.com/q/79147054/2153567
1

In Oracle, you can do it in a single query without any joins using:

SELECT id         AS leaf_id,
       name       AS leaf_name,
       PRIOR id   AS parent_id1,
       PRIOR name AS parent_name1,
       CASE LEVEL WHEN 3 THEN CONNECT_BY_ROOT(id) END AS parent_id2,
       CASE LEVEL WHEN 3 THEN CONNECT_BY_ROOT(name) END AS parent_name2
FROM   categories
WHERE  CONNECT_BY_ISLEAF = 1
START WITH parent_id IS NULL
CONNECT BY PRIOR id = parent_id

Which, for the sample data:

CREATE TABLE categories (id, name, parent_id) AS
SELECT  1, 'Accommodation',  null FROM DUAL UNION ALL
SELECT  2, 'Transport',      null FROM DUAL UNION ALL
SELECT  3, 'Utility',        1 FROM DUAL UNION ALL
SELECT  4, 'Maintenance',    1 FROM DUAL UNION ALL
SELECT  5, 'Private',        2 FROM DUAL UNION ALL
SELECT  6, 'Public',         2 FROM DUAL UNION ALL
SELECT  7, 'Electricity',    3 FROM DUAL UNION ALL
SELECT  8, 'Gas',            3 FROM DUAL UNION ALL
SELECT  9, 'Internet',       3 FROM DUAL UNION ALL
SELECT 10, 'Garden service', 4 FROM DUAL UNION ALL
SELECT 11, 'Repairs',        4 FROM DUAL UNION ALL
SELECT 12, 'Car repayment',  5 FROM DUAL

Outputs:

LEAF_ID LEAF_NAME PARENT_ID1 PARENT_NAME1 PARENT_ID2 PARENT_NAME2
7 Electricity 3 Utility 1 Accommodation
8 Gas 3 Utility 1 Accommodation
9 Internet 3 Utility 1 Accommodation
10 Garden service 4 Maintenance 1 Accommodation
11 Repairs 4 Maintenance 1 Accommodation
12 Car repayment 5 Private 2 Transport
6 Public 2 Transport null null

fiddle

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.