Reputation: 153
I have a table in the following structure. The element with p_id as null is to be displayed as root and all elements mentioned in p_id column as inner and rest of the elements in the id column as leaf. is this the correct way to write the query or is there a more optimized way to write query in postgres sql.
select id, 'Leaf' as node
from tree
where id NOT IN ( select distinct t1.id as node
from tree t1 join tree t2 on t1.id = t2.p_id )
union
select distinct p_id, 'Inner' as node
from tree
where p_id NOT IN( select id from tree where p_id IS NULL)
union
select id, 'Root'
from tree
where p_id is NULL
order by id
The table for the same looks like
I have tried above query and it gives expected result, however, I am not sure if this perfect.
id p_id
-----------
1 2
3 2
6 8
9 8
2 5
8 5
5 (null)
1 Leaf
2 Inner
3 Leaf
5 Root
6 Leaf
8 Inner
9 Leaf
Upvotes: 0
Views: 115
Reputation: 1271141
Usually when working with trees, there is a separate table of nodes. If so, you don't have to use union
for this but can move the logic into the select
:
select id,
(case when not exists (select 1 from tree t where t.id = n.id and t.p_id is null)
then 'root'
when not exists (select 1 from tree t where t.p_id = n.id)
then 'leaf'
else 'inner'
end)
from nodes n;
You can also do this without the nodes
table:
select v.id,
(case when count(*) filter (where t.p_id is null) > 0 then 'root'
when bool_and(v.is_child) then 'leaf'
else 'inner'
end)
from tree t cross join lateral
(values (id, true), (p_id, false)
) v(id, is_child)
where v.id is not null
group by v.id
order by v.id;
Here is a db<>fiddle.
Upvotes: 1