How to Exclude Current or Partial Weeks in PostgreSQL

Let's say you have a simple query that groups by week and looks back at the last 4 weeks:

select
  date_trunc('week', created_at), -- or hour, day, month, year
  count(1)
from users
where created_at > now() - interval '4 weeks'
group by 1;

If you ran this query midweek, say on a Wednesday, the current week would only return data from Monday through Wednesday (~3 days). Given the incomplete week, the last data point would look artificially low:

{{< image src="learn_sql/postgresql/query_with_dip.png" alt="line graph where last data point dips because of incomplete time period" class="ui image mb-2">}}

To avoid this dip (and the inevitable questions from your manager), use the date_trunc() function in the where clause:

select
  date_trunc('week', created_at),
  count(1)
from users
where date_trunc('week', created_at) != date_trunc('week', now())
and created_at > now() - interval '4 weeks'
group by 1;

You now omit any data from the current incomplete week, there's no more dip:

{{< image src="learn_sql/postgresql/query_without_dip.png" alt="line graph where any incomplete time periods are omitted" class="ui image mb-2">}}

There's one more problem. If you ran this query mid-week, the starting point of your "look back period" would be in the middle of the week 4 weeks ago. To guard against incomplete weeks in the beginning of your time range, date_trunc() can help again:

select
  date_trunc('week', created_at),
  count(1)
from users
where date_trunc('week', created_at) != date_trunc('week', now())
and created_at > date_trunc('week',now()) - interval '4 weeks'
group by 1;

Instead of looking back 4 weeks from now(), your query look backs 4 weeks from the beginning of current week. See the difference in below:

select now(); -- Result: 2020-02-05 19:38:26.423589+00
select date_trunc('week',now()); -- Result: 2020-02-03 00:00:00+00

Previous

Next

Ready for a modern SQL editor?