【问题标题】:What is the fastest way to truncate timestamps to 5 minutes in Postgres?在 Postgres 中将时间戳截断为 5 分钟的最快方法是什么?
【发布时间】:2011-11-10 02:10:52
【问题描述】:

Postgres 可以使用 date_trunc 函数舍入(截断)时间戳,如下所示:

date_trunc('hour', val)
date_trunc('minute', val)

我正在寻找一种将时间戳截断到最近的 5 分钟边界的方法,例如,14:26:57 变为 14:25:00。直接的方法是这样的:

date_trunc('hour', val) + date_part('minute', val)::int / 5 * interval '5 min'

由于这是查询的性能关键部分,我想知道这是否是最快的解决方案,或者是否有一些我忽略的快捷方式(与 Postgres 8.1+ 兼容)。

【问题讨论】:

  • 为什么不把它变成一个函数然后索引它,看看它有多快呢?或者只是在完全相同的逻辑上建立索引而不将其包装在索引中。无论哪种方式,您都会知道它的速度有多快。
  • 注意,此方法似乎不会向上舍入到“最近”边界,而是将向下舍入到下一个最低边界。即“2017-04-01 00:04:00”似乎舍入到“2017-04-01 00:00:00”,而不是向上最近边界, 这将是 '2017-04-01 00:05:00'
  • 这个操作很快就会用date_bin函数简单得多

标签: sql performance postgresql datetime


【解决方案1】:

我也在想同样的事情。我找到了两种替代方法,但您建议的方法更快。

我非正式地对我们的一张较大的桌子进行了基准测试。我将查询限制为前 400 万行。我在两个查询之间交替,以避免由于数据库缓存而给一个不公平的优势。


经历纪元/unix 时间

SELECT to_timestamp(
    floor(EXTRACT(epoch FROM ht.time) / EXTRACT(epoch FROM interval '5 min'))
    * EXTRACT(epoch FROM interval '5 min')
) FROM huge_table AS ht LIMIT 4000000

(注意这会产生timestamptz,即使您使用了不知道时区的数据类型)

结果

  • 运行 1:39.368 秒
  • 运行 3:39.526 秒
  • 运行 5:39.883 秒

使用 date_trunc 和 date_part

SELECT 
    date_trunc('hour', ht.time) 
    + date_part('minute', ht.time)::int / 5 * interval '5 min'
FROM huge_table AS ht LIMIT 4000000

结果

  • 运行 2:34.189 秒
  • 运行 4:37.028 秒
  • 运行 6:32.397 秒

系统

  • 数据库版本:x86_64-pc-linux-gnu 上的 PostgreSQL 9.6.2,由 gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 编译,64 位
  • 内核:Intel® Xeon®、E5-1650v2、Hexa-Core
  • 内存:64 GB,DDR3 ECC 内存

结论

您的版本似乎更快。但对于我的特定用例来说还不够快。不必指定小时的优势使 epoch 版本更加通用,并在客户端代码中产生更简单的参数化。它可以像处理2 hour 间隔和5 minute 间隔一样处理date_trunc 时间单位参数。最后,我希望这个时间单位参数改为时间间隔参数。

【讨论】:

  • 第一个版本应该使用 floor 而不是转换为 int - 因为转换可能会导致错误结果 (select 0.9::int -- =1)
  • @Pyrocks 非常感谢。我已经使用 postgres 多年了,现在是我第一次知道将转换为 int “rounds” 而不是 “truncates” 小数。我遇到的所有编程语言都被截断,所以我只是假设它适用于 postgres(和其他数据库?)。我已经更新了答案,但我没有时间重新运行查询。希望不要影响太大。
【解决方案2】:

我认为没有更快的方法。

而且我认为你不应该担心表达式的表现。

执行 (SELECT, UPDATE, ...) 语句所涉及的所有其他内容很可能比该日期/时间计算更昂贵(例如检索行的 I/O)。

【讨论】:

    【解决方案3】:

    对那些想知道的人的完整查询(基于@DNS问题):

    假设您有订单,并且您想按 5min 和 shop_id 的切片来计算它们:

    SELECT date_trunc('hour', created_at) + date_part('minute', created_at)::int / 5 * interval '5 min' AS minute
          , shop_id, count(id) as orders_count
    FROM orders
    GROUP BY 1, shop_id
    ORDER BY 1 ASC
    

    【讨论】:

    • 曾经使用过 Django ORM?关于如何在没有自定义 SQL 查询的情况下执行此操作的任何想法。
    猜你喜欢
    • 2016-07-02
    • 1970-01-01
    • 2018-03-24
    • 2016-03-17
    • 2014-03-25
    • 1970-01-01
    • 2011-06-25
    • 2022-01-15
    • 1970-01-01
    相关资源
    最近更新 更多