在 Postgres 中将时间戳截断为 5 分钟的最快方法是啥?

Posted

技术标签:

【中文标题】在 Postgres 中将时间戳截断为 5 分钟的最快方法是啥?【英文标题】: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+ 兼容)。

【问题讨论】:

为什么不把它变成一个函数然后索引它,看看它有多快呢?或者只是在完全相同的逻辑上建立索引而不将其包装在索引中。无论哪种方式,您都会知道它的速度有多快。 见***.com/a/8963684/287948 注意,此方法似乎不会向上舍入到“最近”边界,而是将向下舍入到下一个最低边界。即“2017-04-01 00:04:00”似乎舍入到“2017-04-01 00:00:00”,而不是向上最近边界, 这将是 '2017-04-01 00:05:00' 这个操作很快就会用date_bin函数简单得多 【参考方案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 查询的情况下执行此操作的任何想法。

以上是关于在 Postgres 中将时间戳截断为 5 分钟的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用 postgres、timescaledb 获取时间戳至少在 5 分钟前的最新行

在postgres中从末尾截断字母?

按时间戳分钟在 postgres 中查询

是否可以通过 SQL(oracle)中的截断时间戳进行排序?

Postgres:获取最大值和最小值,以及它们出现的时间戳

postgres 将子字符串转换为纪元