Получение списка дат в диапазоне от PostgreSQL

Я хотел бы получить список дней между двумя датами (включая их) в базе данных PostgreSQL. Например, если бы у меня было:

  • дата начала: 29 июня 2012 г.
  • дата окончания: 3 июля 2012 г.

тогда результат должен быть:

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

Каким будет лучший способ сделать это в PostgreSQL?

Спасибо.

Ответ 1

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

или даже короче:

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

Ответ 2

Как 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

или заносится в 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

Ответ 3

Это должно сделать это:

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

Если вы не хотите повторять start_date в подзапросе, все становится немного сложнее:

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;

(см. ответ маниакального ответа для гораздо лучшей версии проблемы "без повтора" )

Ответ 4

Для таких вещей обычно удобно иметь таблицу дат в системе.

Как таблица чисел, они могут быть очень полезными и более быстрыми в использовании, чем генерация дат "на лету", особенно при масштабировании до больших наборов данных.

Такая таблица дат с 1900 по 2100 год будет очень малой, поэтому в хранилище не так много.

Редактировать: Dunno, почему это отклоняется, это, вероятно, будет лучшим для производительности. Плюс он имеет так много других преимуществ. Хотите связать заказы с номерами показателей квартала? Его простая связь между таблицами. (Order.OrderDate → Dates.Date → Dates.Quarter → PerformanceTotal.Quarter) и т.д. То же самое касается рабочих дней, например, последнего рабочего дня месяца или первого вторника предыдущего месяца. Как таблица чисел, я бы настоятельно рекомендовал их!

Ответ 5

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

Ответ 6

Если у вас уже есть база данных, которую вы хотите запросить:

SELECT
   TO_CHAR(date_column,'DD Mon YYYY')
FROM
   some_table
WHERE
   date_column BETWEEN '29 Jun 2012' AND '3 JUL 2012'

GROUP BY date_column
ORDER BY date_column

Это приведет к:

"29 Jun 2012"
"30 Jun 2012"
"01 Jul 2012"
"02 Jul 2012"
"03 Jul 2012"

Ответ 7

Эта функция PLpg/SQL выполнит трюк:

CREATE OR REPLACE FUNCTION getDateList(date1 date, date2 date)
RETURNS SETOF date AS
$BODY$
DECLARE
    count integer;
    lower_limit integer :=  0;
    upper_limit integer :=  date2 - date1;
BEGIN
    FOR count IN lower_limit..upper_limit LOOP
        RETURN NEXT date1 + count;
    END LOOP;
    RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE

Ответ 8

Если диапазон дат должен исходить из выражения таблицы, вы можете использовать следующую конструкцию:

DROP TABLE tbl ;
CREATE TABLE tbl (zdate date NOT NULL );
INSERT INTO tbl(zdate) VALUES( '2012-07-01') , ('2012-07-09' );

WITH mima AS (
        SELECT MIN(zdate)::timestamp as mi
        , MAX(zdate)::timestamp as ma
        FROM tbl
        )
SELECT generate_series( mima.mi, mima.ma, '1 day':: interval):: date
FROM mima
        ;

Приводы необходимы, потому что generate_series() не принимает аргументы даты.