Search code examples
sqlpostgresqltime-seriesgreenplumgenerate-series

How to add column to a generate_series query


When looking a date ranges, is there anyway to have generate_series return the starting and ending dates as well?

select 
    '2014-06-05 00:00:00'::timestamp + ('1 month'::INTERVAL * s.a) AS date 
from 
    generate_series(1, cast(extract(month from age('2014-09-05'::date, '2014-06-05'::date)) AS integer), 1) as s(a);

Gives this output

date
2014-07-05 00:00:00
2014-08-05 00:00:00
2014-09-05 00:00:00

This is fine, however I would like to have

start_date     end_date       date
2014-06-05    2014-09-05    2014-07-05 00:00:00
2014-06-05    2014-09-05    2014-08-05 00:00:00
2014-06-05    2014-09-05    2014-09-05 00:00:00

The reason being is that I am extracting multiple start/end pairs from another table but cannot figure out a way of joining them together. I am also using PostgeSQL version 8.2.15 (hence the more complicated generate_series function).

To expand this to my primary problem, I have a table that contains these start and end time pairs.

    start_date         end_date
2014-08-25 00:00:00 2014-09-25 00:00:00
2014-05-16 00:00:00 2014-08-16 00:00:00
2014-09-09 00:00:00 2014-12-09 00:00:00
2014-06-05 00:00:00 2014-07-05 00:00:00
2014-05-19 00:00:00 2014-08-19 00:00:00
2014-05-15 00:00:00 2014-07-15 00:00:00
2014-09-04 00:00:00 2014-11-04 00:00:00

How can I iterate through this table and join it with the expanded date ranges?


Solution

  • Consider upgrading to a current version. Postgres 8.2 is long dead and forgotten.

    For Postgres 8.2 (or later, but there are more elegant solutions in modern Postgres).

    Assuming it's all about dates, not timestamps.

    Providing start_date and end_date once:

    SELECT start_date, end_date
         , (start_date + interval '1 month'
                       * generate_series(1, months))::date AS the_date
    FROM  (  
       SELECT extract(month from age(end_date, start_date))::int AS months
            , start_date, end_date
       FROM (SELECT '2014-06-05'::date AS start_date
                  , '2014-09-05'::date AS end_date
            ) data
       ) sub;
    

    Using column name the_date instead of date which shouldn't be used as identifier.

    Drawing values from table t instead:

    SELECT start_date, end_date
         ,(start_date + interval '1 month'
                      * generate_series(1, months))::date AS the_date
    FROM  (SELECT *, extract(month FROM age(end_date, start_date))::int AS months
           FROM   t) sub;
    

    Without subquery

    SELECT t_id, start_date, end_date
         ,(start_date + interval '1 month'
                      * generate_series(1, extract(month from age(end_date
                                                                , start_date))::int)
                      )::date AS the_date
    FROM   t;
    

    SQL Fiddle for Postgres 8.3.