从表中的开始日期和结束日期在 Postgres 中生成系列

Posted

技术标签:

【中文标题】从表中的开始日期和结束日期在 Postgres 中生成系列【英文标题】:Generate_series in Postgres from start and end date in a table 【发布时间】:2015-05-15 09:50:21 【问题描述】:

我一直在尝试在时间戳字段中从第一个日期到最后一个日期生成一系列日期 (YYYY-MM-DD HH)。我有我需要的generate_series(),但是在尝试从表格中获取开始日期和结束日期时遇到了问题。我有以下粗略的想法:

with date1 as
(
SELECT start_timestamp as first_date
FROM header_table
ORDER BY start_timestamp DESC
LIMIT 1
),
date2 as
(
SELECT start_timestamp as first_date
FROM header_table
ORDER BY start_timestamp ASC    
LIMIT 1
)
    select generate_series(date1.first_date, date2.first_date
                         , '1 hour'::interval)::timestamp as date_hour

from
(   select * from date1
    union
    select * from date2) as foo

Postgres 9.3

【问题讨论】:

一如既往地,请提供您的 Postgres 版本。你提到的“问题”到底是什么? Postgres v9.3 - 抱歉! 【参考方案1】:

试试这个:

with dateRange as
  (
  SELECT min(start_timestamp) as first_date, max(start_timestamp) as last_date
  FROM header_table
  )
select 
    generate_series(first_date, last_date, '1 hour'::interval)::timestamp as date_hour
from dateRange

注意:您希望 2 个日期在 a 行中,而不是在单独的行中。

see this sqlfiddle demo

【讨论】:

【参考方案2】:

改用聚合函数怎么样?

with dates as (
      SELECT min(start_timestamp) as first_date, max(start_timestamp) as last_date
      FROM header_table
     )
select generate_series(first_date, last_date, '1 hour'::interval)::timestamp as date_hour
from dates;

甚至:

select generate_series(min(start_timestamp),
                       max(start_timestamp),
                       '1 hour'::interval
                      )::timestamp as date_hour
from header_table;

【讨论】:

【参考方案3】:

您不需要 CTE,这会比必要的成本更高。 而且您不需要转换为timestamp,当您将timestamp 类型提供给generate_series() 时,结果已经 的数据类型timestamp。详情在这里:

Generating time series between two dates in PostgreSQL

在 Postgres 9.3 或更高版本中,您可以使用 LATERAL 加入:

SELECT to_char(ts, 'YYYY-MM-DD HH24') AS formatted_ts
FROM  (
   SELECT min(start_timestamp) as first_date
        , max(start_timestamp) as last_date
   FROM   header_table
   ) h
  , generate_series(h.first_date, h.last_date, interval '1 hour') g(ts);

可以选择使用to_char() 以获取您提到的格式的文本结果。

这适用于任何 Postgres 版本:

SELECT generate_series(min(start_timestamp)
                     , max(start_timestamp)
                     , interval '1 hour') AS ts
FROM   header_table;

通常会快一点。 在SELECT 列表中调用集合返回函数是一个非标准的SQL 特性,并且被一些人所反对。此外,最终在 Postgres 10 中修复了一些行为异常(尽管不是针对这个简单的案例)。请参阅:

What is the expected behaviour for multiple set-returning functions in SELECT clause?

注意NULL处理的细微差别:

相当于

max(start_timestamp)

通过

获得
ORDER BY start_timestamp DESC NULLS LAST
LIMIT 1

没有NULLS LAST NULL 值按降序排列首先(如果start_timestamp 中有可以为NULL 值)。 last_date 你会得到 NULL 并且你的查询会是空的。

详情:

Why do NULL values come first when ordering DESC in a PostgreSQL query?

【讨论】:

感谢您的彻底帮助,非常感谢。 在评估了 Gordon Linoff 和 Used_By_Already 之后,我接受了这个作为正确答案。他们的答案很棒,而且效果很好。然而,这减少了大约 20-30 毫秒的时间。在一个在数百个表上几乎 24x7 运行的庞大系统中,这将产生影响(尽管很小)。 在第一个代码示例中,g(ts); 指的是什么? @Ryder***s: g(ts) 是表和列别名(省略可选关键字AS)。见:postgresql.org/docs/current/static/…

以上是关于从表中的开始日期和结束日期在 Postgres 中生成系列的主要内容,如果未能解决你的问题,请参考以下文章

根据另一个表中的开始日期和结束日期对值求和

如何在 Postgres 函数中存储日期变量?

在包含多个开始和日期列表的表中查找日期,如果找到则返回默认值

如何使用另一个表中的日期列过滤一个表的开始和结束日期

在不同的表中返回最近的日期

通过只知道没有时间的日期从表中选择 (ORACLE)