J
You can check a DB fiddle of the solution here (without a function just raw SQL): https://www.db-fiddle.com/f/fR2JtQKVcQzrnuZ9SfpcNS/0
For the solution, I am providing some dummy data to show some real output as I don't have your data:
create table if not exists users (
user_id serial primary key,
name text
);
create table if not exists games (
game_id serial primary key,
user_id int references users(user_id),
score numeric,
inserted_at timestamptz
);
insert into users (name)
values ('5w3n'),('CCoX'),('FHWbb8f7'),('NhsX1XcP'),('OPP6A3j3'),('t4N'),('scdHS'),('Adve'),('yqy'),
('fALF'),('J77T'),('TAXQ5B3V'),('GQGtBXV'),('U2yx6o'),('4in74FhL'),('ZsoZs'),('9dn6'),
('ewhi3u6q'),('q18');
insert into games(user_id, score, inserted_at)
values (5,589.53,'2022-01-01'),(9, 48.70,'2022-01-11'),(6, 27.05,'2022-02-01'),
(1,952.81,'2022-02-23'),(11,23.80,'2022-02-15'),(5,732.60,'2022-03-02'),
(7,460.05,'2022-03-09'),(4,402.98,'2022-03-29'),(7,375.06,'2022-03-30'),
(3,325.03,'2022-04-12'),(2,363.32,'2022-04-15'),(12,34.60,'2022-04-19'),
(6,370.70,'2022-04-22'),(6,462.08,'2022-05-01'),(2,575.39,'2022-05-02'),
(13,14.56,'2022-05-03');
The function:
create or replace function get_user_averages(
p_start_date timestamptz,
p_end_date timestamptz default now(),
p_interval interval default '1 month',
p_truncate text default 'month'
)
returns table(
user_id int,
name text,
data numeric[]
)
language plpgsql
as
$$
begin
return query
select x.user_id, x.name, array_agg(x.average order by x.gen)
from (
select
gen,
u.user_id,
u.name,
round(avg(g.score) filter(where gen = date_trunc(p_truncate, g.inserted_at)) ,2) as average
from generate_series(p_start_date, p_end_date, p_interval) gen
cross join games g
join users u on u.user_id = g.user_id
group by gen, u.user_id, u.name
) x
group by x.user_id, x.name
order by x.user_id;
end;
$$;
This function allows you to input your own custom start and end date as well as choosing your own interval (it can be 1 month, 1 day etc.). The output of the array itself (data column) is intentionally ordered (so the array itself is ordered) by the generated dates in ascending order. It might not be a useful feature, however, considering you don't have the dates available in the final output you won't be able to know which average belongs to which generated date.
For this reason I have sorted the array so that your application code may be able to at least do something with the dates if it needs to. I would recommend trying to use a jsonb output instead of array where you would have a {month1:average1, month2:average2 ...} kind of output which may be more useful.
You have to match the p_interval and p_truncate parameters to get the correct output. As one is for date generation, the other specifies the truncation of the timestamptz of the real table's inserted_at (so if you're sending interval '1 month', you should send month in p_truncate parameter)
Calling the function:
select * from get_user_averages('2022-01-01');
The result:
+-------+--------+-----------------------------------+
|user_id|name |data |
+-------+--------+-----------------------------------+
|1 |5w3n |{null,952.81,null,null,null,null} |
|2 |CCoX |{null,null,null,363.32,575.39,null}|
|3 |FHWbb8f7|{null,null,null,325.03,null,null} |
|4 |NhsX1XcP|{null,null,402.98,null,null,null} |
|5 |OPP6A3j3|{589.53,null,732.6,null,null,null} |
|6 |t4N |{null,27.05,null,370.7,462.08,null}|
|7 |scdHS |{null,null,417.56,null,null,null} |
|9 |yqy |{48.7,null,null,null,null,null} |
|11 |J77T |{null,23.8,null,null,null,null} |
|12 |TAXQ5B3V|{null,null,null,34.6,null,null} |
+-------+--------+-----------------------------------+