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;
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