Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting date list in a range in PostgreSQL

I'd like to get the list of days between the two dates (including them) in a PostgreSQL database. For example, if I had:

  • start date: 29 june 2012
  • end date: 3 july 2012

then the result should be:

29 june 2012
30 june 2012 
1 july 2012 
2 july 2012 
3 july 2012

What would be the best way of doing this in PostgreSQL?

Thanks.

like image 318
Javi Avatar asked Jul 09 '12 07:07

Javi


People also ask

How do I search between dates in PostgreSQL?

In Postgresql, we can find the date range between the timestamp using the BETWEEN clause. In this section, we will use the table named journey, and a description of the table is given below.

What is generate series in PostgreSQL?

Enter the simple but handy set returning function of Postgres: generate_series . generate_series as the name implies allows you to generate a set of data starting at some point, ending at another point, and optionally set the incrementing value. generate_series works on two datatypes: integers. timestamps.

Is between inclusive in PostgreSQL?

The PostgreSQL BETWEEN condition will return the records where expression is within the range of value1 and value2 (inclusive).


5 Answers

select CURRENT_DATE + i 
from generate_series(date '2012-06-29'- CURRENT_DATE, 
     date '2012-07-03' - CURRENT_DATE ) i

or even shorter:

select i::date from generate_series('2012-06-29', 
  '2012-07-03', '1 day'::interval) i
like image 175
maniek Avatar answered Oct 20 '22 22:10

maniek


As timestamp:

select generate_series('2012-06-29', '2012-07-03', '1 day'::interval);

    generate_series     
------------------------
 2012-06-29 00:00:00-03
 2012-06-30 00:00:00-03
 2012-07-01 00:00:00-03
 2012-07-02 00:00:00-03
 2012-07-03 00:00:00-03

or casted to date:

select (generate_series('2012-06-29', '2012-07-03', '1 day'::interval))::date;

 generate_series 
-----------------
 2012-06-29
 2012-06-30
 2012-07-01
 2012-07-02
 2012-07-03
like image 39
Clodoaldo Neto Avatar answered Oct 20 '22 23:10

Clodoaldo Neto


This should do it:

select date '2012-06-29' + i
from generate_series(1, (select date '2012-07-3' - date '2012-06-29')) i

If you don't want to repeat the start_date in the subselect things get a bit more complicated:

with min_max (start_date, end_date) as (
   values (date '2012-06-29', date '2012-07-3')
), date_range as (
  select end_date - start_date as duration
  from min_max
)
select start_date + i
from min_max
  cross join generate_series(1, (select duration from date_range)) i;

(See maniek's answer for a much better version of the "no-repeat" problem)

like image 33
a_horse_with_no_name Avatar answered Oct 20 '22 22:10

a_horse_with_no_name


select generate_series('2012-06-29', '2012-07-03', '1 day'::interval)::date;
like image 8
ASGAR HUSSAIN Avatar answered Oct 20 '22 23:10

ASGAR HUSSAIN


For things like this its generally handy to have a dates table in the system.

Just like a numbers table they can be very useful and quicker to use than generating the dates on the fly, especially when you scale up to large data sets.

Such a date table from 1900 to 2100 will be very small, so there isn't much over head in storage.

Edit: Dunno why this is getting voted down, it will probably be the best for performance. Plus it has so many other advantages. Want to link orders to a an quarters performance numbers? Its a simple link between the tables. (Order.OrderDate -> Dates.Date -> Dates.Quarter -> PerformanceTotal.Quarter) etc. Its the same for dealing with working days, like the last working day of a month, or the first Tuesday of the previous month. Like a numbers table, I'd strongly recommend them!

like image 6
BJury Avatar answered Oct 20 '22 23:10

BJury