我有这样的查询,很好地生成两个给定日期之间的一系列日期:
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
它在2004-03-07
和2004-08-16
之间生成162个日期,这就是我想要的。这段代码的问题在于,当两个日期来自不同年份时,例如当我尝试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
;
这应该是最佳方式:
SELECT 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
列表中的set-returns函数:
SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;
AS
关键字在最后一个变体中是必需的,否则Postgres会误解列别名day
。而且我不会在Postgres 10之前建议这个变体 - 至少在同一个SELECT
列表中没有多个set-returns函数:
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(integer,integer,integer) | integer generate_series(integer,integer) | integer generate_series(bigint,bigint,bigint) | bigint generate_series(bigint,bigint) | bigint generate_series(numeric,numeric,numeric) | numeric generate_series(numeric,numeric) | numeric generate_series(timestamp without time zone,timestamp without time zone,interval) | timestamp without time zone generate_series(timestamp with time zone,timestamp with time zone,interval) | timestamp with time zone
(numeric
变种与Postgres 9.5一起添加。)相关的是大胆采取和返回timestamp
/ timestamptz
的最后两个。
正如你所看到的,没有变种采取或返回date
。返回date
需要显式转换。传递timestamp
直接解析为最佳变体,而不会降低到函数类型解析规则,也无需为输入添加额外的强制转换。
timestamp '2004-03-07'
是完全有效的,顺便说一句。省略的时间部分默认为ISO格式的00:00
。
感谢function type resolution,我们仍然可以通过date
。但这需要Postgres的更多工作。从date
到timestamp
以及从date
到timestamptz
的隐含演员。将是模棱两可的,但timestamptz
在“日期/时间类型”中是“首选”。所以match is decided at step 4d.:
运行所有候选项并将接受首选类型(输入数据类型的类型类别)的那些保留在需要进行类型转换的大多数位置。如果没有接受首选类型,请保留所有候选如果只剩下一名候选人,请使用它;否则继续下一步。
除了函数类型解析中的额外工作之外,这还为timestamptz
添加了额外的强制转换。对timestamptz
的演员不仅增加了成本,还会引入DST问题,在极少数情况下导致意外结果。 (DST是一个愚蠢的概念,顺便说一句,不能强调这一点。)相关:
我在演示更昂贵的查询计划的小提琴中添加了演示:
dbfiddle here
有关:
您可以直接生成日期系列。无需使用整数或时间戳:
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