У меня есть такой запрос, который красиво генерирует серию дат между двумя заданными датами:
select date '2004-03-07' + j - i as AllDate
from generate_series(0, extract(doy from date '2004-03-07')::int - 1) as i,
generate_series(0, extract(doy from date '2004-08-16')::int - 1) as j
Он генерирует 162 даты между 2004-03-07
и 2004-08-16
и тем, что я хочу. Проблема с этим кодом в том, что он не даст правильного ответа, если две даты относятся к разным годам, например, когда я пытаюсь 2007-02-01
и 2008-04-01
.
Есть ли лучшее решение?
Ответы:
Может быть выполнено без преобразования в / из int (но вместо этого в / из метки времени)
SELECT date_trunc('day', dd):: date FROM generate_series ( '2007-02-01'::timestamp , '2008-04-01'::timestamp , '1 day'::interval) dd ;
источник
date_trunc
нужен?Чтобы создать серию дат это оптимальный способ:
SELECT t.day::date FROM generate_series(timestamp '2004-03-07' , timestamp '2004-08-16' , interval '1 day') AS t(day);
Дополнительного
date_trunc()
не требуется. Приведение кdate
(day::date
) делает это неявно.Но также нет смысла преобразовывать литералы даты в
date
качестве входного параметра. Наоборот,timestamp
это лучший выбор . Преимущество в производительности небольшое, но нет причин не брать его. И вам не нужно без нужды задействовать правила перехода на летнее время (DST) вместе с преобразованием изdate
вtimestamp with time zone
и обратно. Увидеть ниже.Эквивалентный, менее явный короткий синтаксис:
SELECT day::date FROM generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') day;
Или с функцией возврата набора в
SELECT
списке:SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;
AS
Ключевое слово требуется в последнем варианте, Postgres бы извратить псевдоним столбца вday
противном случае. И я бы не советовал этот вариант до Postgres 10 - по крайней мере, с более чем одной функцией возврата набора в одномSELECT
списке:(Кроме того, последний вариант обычно самый быстрый с небольшим отрывом.)
Почему
timestamp [without time zone]
?Существует ряд перегруженных вариантов
generate_series()
. В настоящее время (Postgres 11):(
numeric
варианты были добавлены в Postgres 9.5.) Соответствующие два последних выделены жирным шрифтом take и returntimestamp
/timestamptz
.Нет варианта взять или вернуть
date
. Для возврата необходимо явное приведениеdate
. Вызов сtimestamp
аргументами преобразуется в лучший вариант напрямую, без перехода в правила разрешения типов функций и без дополнительного приведения для ввода.timestamp '2004-03-07'
совершенно верно, кстати. Пропущенная часть времени по умолчанию соответствует00:00
формату ISO.Благодаря разрешению типа функции мы все еще можем пройти
date
. Но это требует от Postgres дополнительной работы. Существует неявное приведение отdate
до,timestamp
а также от одногоdate
доtimestamptz
. Было бы неоднозначным, ноtimestamptz
является «предпочтительным» среди «типов даты / времени». Таким образом, совпадение определяется на шаге 4d. :В дополнение к дополнительной работе по разрешению типов функций, это добавляет дополнительное приведение к
timestamptz
- что не только увеличивает стоимость, но также может создавать проблемы с DST, приводящие в редких случаях к неожиданным результатам. (Летнее время - дебильная концепция, кстати, не могу не подчеркнуть этого достаточно.)Я добавил в скрипт демонстрационные ролики, показывающие более дорогой план запроса:
db <> скрипка здесь
Связанный:
источник
SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') :: DATE AS day;
AS t(day)
вSELECT * FROM func() AS t(day)
это таблица и столбец псевдоним. ВAS
этом контексте ключевое слово является необязательным шумом. См .: stackoverflow.com/a/20230716/939860Вы можете создавать ряды непосредственно с датами. Не нужно использовать целые числа или временные метки:
select date::date from generate_series( '2004-03-07'::date, '2004-08-16'::date, '1 day'::interval ) date;
источник
Вы также можете использовать это.
select generate_series ( '2012-12-31'::timestamp , '2018-10-31'::timestamp , '1 day'::interval) :: date
источник