在 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 分钟前的最新行