Home > Net >  SQL tree recursive mean of children values
SQL tree recursive mean of children values

Time:06-10

I am currently designing a catalogue system. It has 2 types of items: categories and items themselves. There also may be nested categories and some items may have a parent which is always some category.
So the table looks like:

create table items
(
    id        uuid,
    parent    uuid,
    name      text,
    type      text,
    cost      int,

    primary key (id),
    constraint constraint_on_parent
        foreign key (parent)
            references items (id)
);

Another thing to mention is that all the category items have a cost equals to null (well, after all, you cannot buy a category itself, right?).
Now I need to come up with an sql query which would, given an item id, return itself and all its children in case it is a category. Also if it is a category, then I would like to get the average price of its children (also applies to the sub-categories).
So far I have managed to create a recursive query which retrieves itself and the children:

with recursive query as (
    select id, name, type, cost
    from items
    where id=$item_id
    union all
    select it.id, it.name, it.type, it.cost
    from items it inner join query q on q.id = it.parent
)
select id, name
from children

However, now I am wondering, how can I change it to calculate the mean price of thecategory and its subcateogries recursively?
Also, I am using PostgreSQL 14.

Edit 1: Sample data and desired output
Sample data

id parent name type cost
uuid1 null root category null
uuid2 uuid1 item1 item 100
uuid3 uuid1 subcategory1 category null
uuid4 uuid3 item2 item 200
uuid5 uuid3 item3 item 300

Desired ouput
When run the described query against uuid3 I expect to get something like

id parent name type cost
uuid3 uuid1 subcategory1 category 250
uuid4 uuid3 item2 item 200
uuid5 uuid3 item3 item 300

This output looks like the 3 last rows of the sample data, except for the first row, the category, having a price equal to the mean price of its children
When run the described query against uuid1 I expect to get

id parent name type cost
uuid1 null root category 200
uuid2 uuid1 item1 item 100
uuid3 uuid1 subcategory1 category 250
uuid4 uuid3 item2 item 200
uuid5 uuid3 item3 item 300

Here subcategory1 price is the mean of item2 and item3 costs, root price is the mean of item1, item2 and items3 costs.
Also, if the category has no items in it, its price should remain null

CodePudding user response:

You can use ARRAY to store path to the item

with recursive query as (
    select id, name, type, cost, array[id] path
    from items
    where id='uuid1'
    union all
    select it.id, it.name, it.type, it.cost, array_append(path, it.id)
    from items it inner join query q on q.id = it.parent
)
select  t1.id, t1.name, t1.type, avg(t2.cost) cost
from query t1
left join query t2 on t2.path @> Array[t1.id]
group by t1.id, t1.name, t1.type;

db<>fiddle

Returns

id  name    type    cost
uuid1   root    category    200.0000000000000000
uuid2   item1   item    100.0000000000000000
uuid3   subcategory1    category    250.0000000000000000
uuid4   item2   item    200.0000000000000000
uuid5   item3   item    300.0000000000000000
  • Related