Чтобы создать серию дат это оптимальный способ:
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):
SELECT oid::regprocedure AS function_signature
, prorettype::regtype AS return_type
FROM pg_proc
where proname = 'generate_series';
function_signature | return_type
: ------------------------------------------------- ------------------------------- | : --------------------------
generate_series (целое, целое, целое) | целое число
generate_series (целое, целое) | целое число
generate_series (bigint, bigint, bigint) | Bigint
generate_series (bigint, bigint) | Bigint
generate_series (числовой, числовой, числовой) | числовой
generate_series (числовой, числовой) | числовой
generate_series (отметка времени без часового пояса, отметка времени без часового пояса, интервал) | отметка времени без часового пояса
generate_series (временная метка с часовым поясом, временная метка с часовым поясом, интервал) | метка времени с часовым поясом
( numeric
варианты были добавлены в Postgres 9.5.) Соответствующие два последних выделены жирным шрифтом take и return timestamp
/ timestamptz
.
Нет варианта взять или вернутьdate
. Для возврата необходимо явное приведение date
. Вызов с timestamp
аргументами преобразуется в лучший вариант напрямую, без перехода в правила разрешения типов функций и без дополнительного приведения для ввода.
timestamp '2004-03-07'
совершенно верно, кстати. Пропущенная часть времени по умолчанию соответствует 00:00
формату ISO.
Благодаря разрешению типа функции мы все еще можем пройти date
. Но это требует от Postgres дополнительной работы. Существует неявное приведение от date
до, timestamp
а также от одного date
до timestamptz
. Было бы неоднозначным, но timestamptz
является «предпочтительным» среди «типов даты / времени». Таким образом, совпадение определяется на шаге 4d. :
Просмотрите всех кандидатов и оставьте те, которые принимают предпочтительные типы (из категории типа входных данных) в большинстве позиций, где потребуется преобразование типов. Оставьте всех кандидатов, если ни один из них не принимает предпочтительные типы. Если остается только один кандидат, используйте его; иначе переходите к следующему шагу.
В дополнение к дополнительной работе по разрешению типов функций, это добавляет дополнительное приведение к timestamptz
- что не только увеличивает стоимость, но также может создавать проблемы с DST, приводящие в редких случаях к неожиданным результатам. (Летнее время - дебильная концепция, кстати, не могу не подчеркнуть этого достаточно.)
Я добавил в скрипт демонстрационные ролики, показывающие более дорогой план запроса:
db <> скрипка здесь
Связанный: