PostgreSQL getting daily, weekly, and monthly averages of the occurrences of an event in one query PostgreSQL getting daily, weekly, and monthly averages of the occurrences of an event in one query postgresql postgresql

PostgreSQL getting daily, weekly, and monthly averages of the occurrences of an event in one query


I'd write the query in a way like this:

select event, daily_avg, weekly_avg, monthly_avgfrom (    select event, avg(count) monthly_avg    from (        select event, count(*)        from tracking_stuff        where event in ('thing1', 'thing2', 'thing3')        group by event, date_trunc('month', created_at)    ) s    group by 1) monthlyjoin (    select event, avg(count) weekly_avg    from (        select event, count(*)        from tracking_stuff        where event in ('thing1', 'thing2', 'thing3')        group by event, date_trunc('week', created_at)    ) s    group by 1) weekly using(event)join (    select event, avg(count) daily_avg    from (        select event, count(*)        from tracking_stuff        where event in ('thing1', 'thing2', 'thing3')        group by event, date_trunc('day', created_at)    ) s    group by 1) daily using(event)order by 1;

If the where condition eliminates a significant portion of the data (say more than a half) the use of cte could slightly speed up the query execution:

with the_data as (    select event, created_at    from tracking_stuff    where event in ('thing1', 'thing2', 'thing3')    )select event, daily_avg, weekly_avg, monthly_avgfrom (    select event, avg(count) monthly_avg    from (        select event, count(*)        from the_data        group by event, date_trunc('month', created_at)    ) s    group by 1) monthly--  etc ... 

Just for curiosity I've done a test on the data:

create table tracking_stuff (event text, created_at timestamp);insert into tracking_stuff    select 'thing' || random_int(9), '2016-01-01'::date+ random_int(365)    from generate_series(1, 1000000);

In every query I've replaced thing with thing1, so the queries eliminate about 2/3 of rows.

Average execution time of 10 tests:

Original query          1106 msMy query without cte    1077 msMy query with cte        902 msClodoaldo's query       5187 ms


In 9.5+ use grouping sets

The data selected by the FROM and WHERE clauses is grouped separately by each specified grouping set, aggregates computed for each group just as for simple GROUP BY clauses, and then the results returned

select event,    avg(total) filter (where day is not null) as avg_day,    avg(total) filter (where week is not null) as avg_week,    avg(total) filter (where month is not null) as avg_month    from (    select        event,        date_trunc('day', created_at) as day,        date_trunc('week', created_at) as week,        date_trunc('month', created_at) as month,        count(*) as total    from tracking_stuff    where event in ('thing','thing2','thing3')    group by grouping sets ((event, 2), (event, 3), (event, 4))) sgroup by event