【问题标题】:Postgres SQL select a range of records spaced out by a given intervalPostgresql 选择由给定间隔间隔的一系列记录
【发布时间】:2011-03-15 03:27:59
【问题描述】:

我正在尝试确定是否可以仅使用 sql for postgres 来选择给定时间间隔内的时间排序记录范围。

假设我有 60 条记录,给定小时内的每分钟一条记录。我想在那一小时每隔 5 分钟选择一次记录。结果行应该是 12 条记录,每条记录间隔 5 分钟。

目前这是通过选择全部记录范围然后循环遍历结果并以给定间隔提取记录来完成的。我想看看我是否可以在 sql 中完全做到这一点,因为我们的数据库很大,我们可能要处理数万条记录。

有什么想法吗?

【问题讨论】:

  • 请展示你的成就——也许它会让你的问题更清楚

标签: sql postgresql


【解决方案1】:

是的,你可以。一旦你掌握了它,它真的很容易。我认为它是 SQL 的瑰宝之一,并且在 PostgreSQL 中特别容易,因为它具有出色的时间支持。通常,复杂的函数可以在 SQL 中变成非常简单的查询,可以正确扩展和索引。

这使用 generate_series 来绘制间隔 1 分钟的示例时间戳。外部查询然后提取分钟并使用模来查找相隔 5 分钟的值。

select
    ts,
    extract(minute from ts)::integer as minute

    from
    ( -- generate some time stamps - one minute apart
        select
            current_time + (n || ' minute')::interval  as ts
        from generate_series(1, 30) as n
    ) as timestamps
    -- extract the minute check if its on a 5 minute interval
    where extract(minute from ts)::integer % 5 = 0
    -- only pick this hour 
    and extract(hour from ts) = extract(hour from current_time)
;
         ts         | minute 
--------------------+--------
 19:40:53.508836-07 |     40
 19:45:53.508836-07 |     45
 19:50:53.508836-07 |     50
 19:55:53.508836-07 |     55

请注意,如何在 where 子句(表达式的值将构成索引的地方)添加计算索引可以显着提高速度。在这种情况下可能不是很有选择性,但需要注意。

我曾经在 PostgreSQL 中编写过一个预订系统(它有很多时间逻辑,其中日期间隔不能重叠)并且从来不必诉诸迭代方法。

http://www.amazon.com/SQL-Design-Patterns-Programming-Focus/dp/0977671542 是一本很棒的书,里面有很多区间示例。现在在书店很难找到,但很值得。

【讨论】:

  • 我认为这个问题类似于 GPS 提要,我们希望每 5 分钟间隔提取一个数据点。
  • 谢谢,这很有帮助。
  • 此功能需要哪个版本的 PostgreSQL?
【解决方案2】:

提取分钟,转换为int4,看看除以5的余数是否为0:

select * 
  from TABLE 
  where int4 (date_part ('minute', COLUMN)) % 5 = 0; 

【讨论】:

  • 这与我提出的解决方案非常接近。实际上,记录每秒或更快地进入一次,并且记录的间距是在运行时确定的。目前我正在做这样的事情: CAST (extract(epoch from date_trunc('second', search_time)) AS integer) % 300 = 0
  • 只能编辑 5 分钟...让我再试一次。我仍在确定哪些适合我的需要。谢谢你的回复。这与我提出的可能解决方案非常接近。我的记录每秒存储一次或更快,记录的间距在运行时确定。目前我正在做这样的事情:
  • select * from table where id = 'record_id' and CAST (extract(epoch from date_trunc('second', the_time)) AS integer) % 300 = 0 order by the_time 本例中的 300 返回以 5 分钟为增量记录,但在某些情况下,我正在寻找 10 秒增量或 15 分钟增量。正如上面某人所指出的那样,这样做的缺点是记录是基于“五人制”返回的。
  • 问题出在哪里,我不明白?同一秒钟的两次报告?从 5 秒到 10 秒再到 15 秒(为什么)?
  • 好吧,我四舍五入到秒,因为如果我四舍五入到分钟,我将在同一分钟有 60 条或更多记录,或者每次 60 条记录 % 5 = 0。四舍五入到秒让我四舍五入但非常独特的结果,% 300 每 5 分钟给我一个预期的记录。然而,正如其他人所提到的,这些记录甚至出现了“5”次。例如:如果我要在 1:03 和 1:18 的时间范围内查找五分钟记录,则此解决方案将为我提供 1:05、1:10 和 1:15。但我想要 1:03、1:08 和 1:13。
【解决方案3】:
  • 如果间隔不是基于时间的,而您只需要每 5 行;或
  • 如果时间规律并且您总是每分钟有一个记录

以下每 5 条记录为您提供一条记录

select *
from
(
  select *, row_number() over (order by timecolumn) as rown
  from tbl
) X
where mod(rown, 5) = 1

如果您的时间记录不规则,那么您需要生成一个时间序列(在另一个答案中给出)并将其加入您的表中,按时间列(来自该系列)分组并从您的小于时间列的表。

select thetimeinterval, max(timecolumn)
from ( < the time series subquery > ) X
left join tbl on tbl.timecolumn <= thetimeinterval
group by thetimeinterval

并进一步将其加入表中以获取完整记录(假设唯一时间)

select t.* from
tbl inner join
(
    select thetimeinterval, max(timecolumn) timecolumn
    from ( < the time series subquery > ) X
    left join tbl on tbl.timecolumn <= thetimeinterval
    group by thetimeinterval
) y on tbl.timecolumn = y.timecolumn

【讨论】:

  • 您会产生不必要的窗口函数开销。如果条件快到了,那么没有理由添加更多信息。它还要求所有数据都存在,并且永远不会有缺失的行,但情况可能并非如此。可能最好只用给出的内容来回答问题。
  • @nate 但我正在解决的问题是,您的回答对于以 5 分钟间隔选择每分钟 60 条记录中的 12 条没有任何作用。你只是在构建一个时间序列。
  • 谢谢理查德。在我的情况下,间隔是基于时间的,但它们可能比每分钟快得多。在生产中,它们将是每秒一个,并且不能保证在给定的秒内可能没有两个。我将研究其中的几个解决方案,包括仅选择每 5 行的选项。我刚刚对其进行了测试,效果很好。
【解决方案4】:

这个怎么样:

select min(ts), extract(minute from ts)::integer / 5 
   as bucket group by bucket order by bucket; 

如果您在一分钟内有两个读数,或者您的读数跳过一分钟,则这样做的好处是做正确的事情。与其使用min,不如使用first() 聚合函数之一——您可以在此处找到代码:

http://wiki.postgresql.org/wiki/First_%28aggregate%29

【讨论】:

    【解决方案5】:

    这假设你的五分钟间隔是“五分钟”,可以这么说。也就是说,您想要 07:00、07:05、07:10,而不是 07:02、07:07、07:12。它还假设您在同一分钟内没有两行,这可能不是一个安全的假设。

    select your_timestamp
    from your_table
    where cast(extract(minute from your_timestamp) as integer) in (0,5);
    

    如果您在同一分钟内可能有两行带有时间戳,例如

    2011-01-01 07:00:02
    2011-01-01 07:00:59
    

    那么这个版本更安全。

    select min(your_timestamp)
    from your_table
    group by (cast(extract(minute from your_timestamp) as integer) / 5)
    

    将其中任何一个包装在一个视图中,您可以将其加入到您的基表中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-10
      • 1970-01-01
      • 2014-02-15
      • 2014-09-12
      • 2019-11-12
      • 2015-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多