生成PostgreSQL中两个日期之间的时间序列

f.a*_*uri 72 postgresql date time-series postgresql-9.1 generate-series

我有这样的查询,很好地生成两个给定日期之间的一系列日期:

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
Run Code Online (Sandbox Code Playgroud)

它在2004-03-07和之间生成162个日期2004-08-16,这就是我想要的.这段代码的问题是,它不会得到正确的答案时,这两个日期都不同年份,例如,当我尝试2007-02-012008-04-01.

有更好的解决方案吗?

wil*_*ser 129

可以在不转换为/从int转换的情况下完成(但是转换为/从时间戳转换)

SELECT date_trunc('day', dd):: date
FROM generate_series
        ( '2007-02-01'::timestamp 
        , '2008-04-01'::timestamp
        , '1 day'::interval) dd
        ;
Run Code Online (Sandbox Code Playgroud)

  • 不需要“date_trunc”,因为您已经使用“::date”将其强制为“date”类型。不管有没有它都会产生相同的结果。 (4认同)
  • 这只是介绍。它消除了时间戳的时间部分的打印,在这种情况下它始终为零。 (3认同)
  • 为什么需要`date_trunc`? (2认同)

Erw*_*ter 54

有两个答案(到目前为止).两者都有效,但两者都不是最理想的.这是第三个:

SELECT t.day::date 
FROM   generate_series(timestamp '2004-03-07'
                     , timestamp '2004-08-16'
                     , interval  '1 day') AS t(day);
Run Code Online (Sandbox Code Playgroud)
  • 无需额外的date_trunc().强制转换为date(day::date).

  • 但是将日期文字转换date为输入参数也没有意义.Au逆转,timestamp这里的最佳选择.性能的优势很小,但没有理由不采取它.并且你不必不必要地涉及与数据类型相结合的DST规则date.见下面的解释.

更短的等价物:

SELECT day::date 
FROM   generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') day;
Run Code Online (Sandbox Code Playgroud)

或者甚至使用timestamp with time zone列表中的set-returns函数:

SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;
Run Code Online (Sandbox Code Playgroud)

SELECT关键字要求在这里,因为列别名AS否则会被误解.

建议在Postgres 10之前使用最后一个,至少在同一个day列表中没有多个set-returns函数.看到:

为什么?

有许多重载的变种SELECT.目前(Postgres 10):

SELECT oid::regprocedure   AS function_signature
     , prorettype::regtype AS return_type
FROM   pg_proc
where  proname = 'generate_series';
Run Code Online (Sandbox Code Playgroud)
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

timestamp [without time zone]Postgres 9.5添加了变换和返回.但这里相关的唯一因素是最后两个粗体回吐和返回generate_series()/ numeric.

如您所见,没有变体采取或返回timestamp.这就是为什么我们想要返回时需要一个明确的演员timestamptz.date直接将解析传递给正确的函数,而不必下载到函数类型解析规则中,也不需要为输入添加额外的强制转换.

并且date完全有效.timestamp如果省略,则时间部分默认为.

由于功能类型分辨率,我们仍然可以通过timestamp '2004-03-07'.但这需要Postgres的更多工作.有一个隐含的00:00date以及从datetimestamp.将是模棱两可的,但在"日期/时间类型"中date"首选 ".所以匹配在步骤4d决定.:

运行所有候选项并将接受首选类型(输入数据类型的类型类别)的那些保留在需要进行类型转换的大多数位置.如果没有接受首选类型,请保留所有候选 如果只剩下一名候选人,请使用它; 否则继续下一步.

除了函数类型解析的额外工作之外,还增加了额外的强制转换timestamptz.演员timestamptz不仅增加了成本,还会引入夏令时(DST)问题,在极少数情况下会导致意外结果.(DST是一个愚蠢的概念,顺便说一句,不能强调这一点.)相关:

我向小提琴添加了演示,以显示更昂贵的查询计划:

dbfiddle 在这里

有关:

  • 更短的版本:`SELECT generate_series(timestamp'2004-03-07','2004-08-16','1 day'):: DATE AS day;` (3认同)
  • t(day) 语法的含义是什么? (2认同)
  • @rendang:“SELECT * FROM func() AS t(day)”中的“AS t(day)”是表和列的别名。在这种情况下,“AS”关键字是可选的噪音。请参阅:/sf/answers/1416150151/ (2认同)

fbo*_*tti 33

您可以直接生成日期系列.无需使用整数或时间戳:

select date::date 
from generate_series(
  '2004-03-07'::date,
  '2004-08-16'::date,
  '1 day'::interval
) date;
Run Code Online (Sandbox Code Playgroud)

  • 根据您所在的时区,这可能会返回意外的结果。我遇到了这个问题。请改用时间戳。设置会话时区 '美国/圣保罗' SELECT d::date FROMgenerate_series('2019-11-01'::date, '2019-11-03'::date, '1 day') d SELECT d::date FROMgenerate_series('2019-11-01'::日期,'2019-11-04'::日期,'1 天') d (3认同)