I have a Postgres table containing peoples' names, their age, and the number of marbles they own. The example has only two rows for simplicity, but that could be more.
name | age | marbles |
---|---|---|
Alice | 4 | 10 |
Bob | 7 | 20 |
I would like to retrieve the number of marbles per age group, where the number of marbles is distributed to each age group, proportionally1 to the person's age. The age groups can vary, but are known at query time2.
The output would look like this:
age_group | marbles |
---|---|
0 | 2 (1/5 of Alice's marbles) |
5 | 20 (4/5 of Alice's marbles, 3/5 of Bob's marbles) |
10 | 8 (2/5 of Bob's marbles) |
Is there an elegant way of querying my table to achieve this?
1To be specific, the proportion of marbles per age group for one person would be:
marbles_higher_age_group = (age - lower_age_group) / (higher_age_group - lower_age_group) * marbles
marbles_lower_age_group = marbles - marbles_higher_age_group
2When querying the table, the age groups are known. They can differ over multiple queries and may be arbitrarily chosen. For instance, one query might use age groups [0,5,10]
, while the next query uses [0,1,4,8,12]
.
CodePudding user response:
For an arbitrary set of age groups stored in a table
select ag, sum(agm) marbles
from marbles m
join (
select age_group low, lead(age_group) over(order by age_group) high
from age_groups ag
) a on a.high >= m.age and a.low <= m.age
, lateral (
select a.low ag , 1.0 * m.marbles * (a.high - m.age)/(a.high - a.low) agm
union all
select a.high, 1.0 * m.marbles * (m.age - a.low)/(a.high - a.low)
) mm
group by ag
order by ag;
Alternatively you can provide an array of age groups in the query
select ag, sum(agm) marbles
from marbles m
join (
select age_group low, lead(age_group) over(order by age_group) high
from unnest(array[1,2,4,8,12]) age_groups(age_group)
) a on a.high >= m.age and a.low <= m.age
, lateral (
select a.low ag, 1.0 * m.marbles * (a.high - m.age)/(a.high - a.low) agm
union all
select a.high, 1.0 * m.marbles * (m.age - a.low)/(a.high - a.low)
) mm
group by ag
order by ag;