Home > Blockchain >  Postgres recursive CTE with Group?
Postgres recursive CTE with Group?

Time:09-21

I tried to find out the masterIds as Array. please find the below

create temp table location as
select 1 as id,'World' as name,null as main_id
union all
select 2,'Asia',1
union all
select 3,'Africa',1
union all
select 4,'India',2
union all
select 5,'Sri Lanka',2
union all
select 6,'Mumbai',4
union all
select 7,'South Africa',3

Table like

Id Name Main Id
1 World null
2 Asia 1
3 Africa 1
4 India 2
5 Sri Lanka 2
6 Mumbai 4
7 South Africa 3
8 Cape Town 7

And My expected result mentioned below:

Id Name Main Id MasterIds Arrayint
1 World null 1
2 Asia 1 1,2
3 Africa 1 1,3
4 India 2 1,2,4
5 Sri Lanka 2 1,2,5
6 Mumbai 4 1,2,4,6
7 South Africa 3 1,3,7
8 Cape Town 7 1,3,7,8

And My code is handling only one row but i need this for entire table. please find my code below:

 with recursive
    cte as (
    select * from location  where id=4
    union all
    select g.*  from cte join location g on g.id  = cte.main_id
    )
    select array_agg(id)  from cte l

CodePudding user response:

You seem to want an array of all ancestors (including id) for each id. Here is one attempt:

with recursive cte as (
    select id, name, main_id from location
    union all
    select cte.id, g.name, g.main_id  
    from cte join location g on g.id  = cte.main_id
)
select id, array[id] || array_agg(main_id)  
from cte l
where main_id is not null
group by id

See Fiddle

For each iteration in the recursive cte, we keep the id that we started from and traverse the tree upwards (i.e. add next ancestor to the set of ancestors). At the end we create one group for each id and aggregate all ancestors into the array.

Worth noting is that the set of ancestors is unordered, so you may end up with a "path" that is out of order. You can fix that by providing an order, here I use the level relative to the id:

 with recursive
    cte as (
    select id, name, main_id, 0 as lvl from location
    union all
    select cte.id, g.name, g.main_id, lvl 1  from cte join location g on g.id  = cte.main_id
    )
    select id, array[id] || array_agg(main_id order by lvl)
    from cte l
    where main_id is not null
    group by id
  • Related