Инкрементные даты PostgreSQL?

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

ID | Date       | Bla
1  | 2013-05-01 | 1
2  | 2013-05-02 | 2
3  | 2013-05-03 | 3
4  | 2013-05-05 | 4

Обратите внимание, что отсутствует дата: 2014-05-04. Как мне изменить следующий запрос:

SELECT * 
FROM table 
where DATE >= '2013-05-01' AND DATE <= '2013-05-05'

Так что я бы получил следующий вывод:

ID   | Date       | Bla
1    | 2013-05-01 | 1
2    | 2013-05-02 | 2
3    | 2013-05-03 | 3
null | 2013-05-04 | null
4    | 2013-05-05 | 4

Это возможно?


person coderama    schedule 04.07.2013    source источник


Ответы (3)


Вы можете присоединиться к выходу generate_series:

select
    '2013-05-01'::date + g.o AS "date with offset"
from
    generate_series(0, 30) AS g(o)

Выход:

"2013-05-01"
"2013-05-02"
"2013-05-03"
...
"2013-05-29"
"2013-05-30"
"2013-05-31"

Или... более простой метод после определения новой хранимой процедуры :)

CREATE OR REPLACE FUNCTION generate_series(date, date) RETURNS
SETOF date AS $$
SELECT $1 + g.s
FROM generate_series(0, ($2 - $1)) AS g(s);
$$ LANGUAGE SQL IMMUTABLE;

Просто назовите это так:

SELECT * FROM generate_series(start_date, end_date);
person Wolph    schedule 04.07.2013
comment
Как это можно использовать, если у вас есть только даты начала и окончания? - person coderama; 04.07.2013
comment
Используйте что-то вроде этого: generate_series(0, (end_date - start_date)::integer)` - person Wolph; 04.07.2013
comment
Если вы используете Postgres 8.4 или более позднюю версию, вам не нужно определять пользовательскую функцию generate_series, так как есть собственная generate_series(from_timestamp, to_timestamp, step_interval) variant (в этом случае просто используйте '1 day'::interval в качестве шага). - person IMSoP; 04.07.2013
comment
Извините за редактирование, но этот длинный список меня действительно раздражал :) - person Clodoaldo Neto; 04.07.2013
comment
@ClodoaldoNeto: достаточно честно :) - person Wolph; 04.07.2013
comment
@IMSoP: правда, но имхо удобнее использовать, так как вам больше не нужно кастовать :) - person Wolph; 04.07.2013
comment
@WoLpH '1 day'::interval на самом деле не приведение как таковое, а просто типизированный литерал. Конечно, вы можете определить generate_series(date, date) как SELECT g.s FROM generate_series($1, $2, '1 day'::interval) AS g(s);, если вам действительно не нравится вводить дополнительный параметр. - person IMSoP; 04.07.2013
comment
@IMSoP: правда, но отсутствие необходимости думать о типах всегда заставляет меня чувствовать себя немного спокойнее, я полагаю, это просто умственная вещь ... - person Wolph; 05.07.2013

select *
from 
    (
        select generate_series(
            '2013-05-01'::date, '2013-05-05', '1 day'
            )::date
    ) s("date")
    left join
    t using ("date")

Замените оба "date" фактическим именем столбца.

person Clodoaldo Neto    schedule 04.07.2013
comment
+1 Это лучший код. Но некоторые пояснения и, возможно, ссылка на руководство для generate_series() было бы здорово. - person Erwin Brandstetter; 04.07.2013

Вам нужно внешне присоединиться к вашей таблице против «списка дат»:

with all_dates (some_date) as (
    select date '2013-05-01' + i 
    from generate_series(0, 10) i  -- adjust here to the range of dates you need.
) 
select t.id, 
       ad.some_date, -- you need to take the actual date from generated ones
       t.bla
from all_dates ad
  left join the_table t on ad.some_date = t.date
where ad.some_date between date '2013-05-01' and date '2013-05-05';

Кстати: date — ужасное имя для столбца. Помимо того, что это зарезервированное слово, оно также ничего не говорит о том, что это за «дата».

person a_horse_with_no_name    schedule 04.07.2013
comment
Как показано в ответе Клодоальдо Нето, generate_series может изначально создавать серию дат, несколько упрощая CTE и упрощая полное удаление предложения WHERE в пользу правильно выбранного диапазона дат в CTE. Тем не менее, красиво изложенный ответ. - person IMSoP; 04.07.2013
comment
О, и я бы предположил, что DATE - это анонимное/примерное имя столбца, поскольку таблица в запросе упоминается как TABLE, что маловероятно для таблицы :) - person IMSoP; 04.07.2013
comment
@IMSoP a_horse точно знает о date перегрузке generate_series. Вероятно, он думает, использует ли OP более старую версию postgresql только с целочисленной перегрузкой generate_series - person Clodoaldo Neto; 04.07.2013
comment
@ClodoaldoNeto: если честно: я всегда забываю о дате версии generate_series();) - person a_horse_with_no_name; 04.07.2013
comment
Что касается управления версиями, оно доступно с версии 8.4, которая вышла 4 года назад и на данный момент является самой старой полностью поддерживаемой версией, поэтому можно с уверенностью предположить, что она будет и сейчас. :) - person IMSoP; 04.07.2013