PostgreSQL, summing multiple date columns over multiple rows

Tags: , , , ,



Let’s assume the following table:

CREATE TABLE assumption ( datetime_1 TIMESTAMP, datetime_2 TIMESTAMP, datetime_3 TIMESTAMP); 

Now I want to know the total amount of times a month is set in any row or column combined as long as the date is in the future.

Right now I have:

  SELECT
    COALESCE(
      SUM(
          COALESCE(
               datetime_1 > NOW() AND EXTRACT(MONTH FROM datetime_1) = 1,
               FALSE
          )::INT,
          COALESCE(
               datetime_2 > NOW() AND EXTRACT(MONTH FROM datetime_2) = 1,
               FALSE
          )::INT,
          COALESCE(
               datetime_3 > NOW() AND EXTRACT(MONTH FROM datetime_3) = 1,
               FALSE
          )::INT
      ), 0) AS january_count,
      .... AS february_count,
      .... AS march_count,
      .... AS etc
    FROM assumption;

This works and returns the right result, yet it is rather bloated and it returns me a single row with a column for every month. As in real life this query is a bit more complex and I would rather have a result that would give me a row for each month (So I can add more fields to every monthly row)

Is there any thing I am missing, any way I can improve this?

Answer

Do you want a lateral join?

select date_trunc('month', d.dt) dt_month, count(*) cnt
from assumption a
cross join lateral (values (datetime_1), (datetime_2), (datetime_3)) d(dt)
where dt > now()
group by date_trunc('month', d.dt)

Truncating the date to the first day of the month would seem more useful that extracting the month (if your data spreads over several years in the future, the result do differ). But if you do mean extracting the month, then:

select extract(month from d.dt) dt_month, count(*) cnt
from assumption a
cross join lateral (values (datetime_1), (datetime_2), (datetime_3)) d(dt)
where dt > now()
group by extract(month from d.dt)

Finally, if you want a row for each month, even those that have no timestamp in the future, then:

select extract(month from d.dt) dt_month, count(*) filter(where d.dt > now()) cnt
from assumption a
cross join lateral (values (datetime_1), (datetime_2), (datetime_3)) d(dt)
group by extract(month from d.dt)


Source: stackoverflow