我有一张包含数百万条记录的表,其中每 10 分钟精确分组一次
tl; dr:对于不耐烦的人,请参阅答案中的最后一个查询,这是真正的解决方案,其他人则逐步了解如何到达那里。此外,所有查询 + 模式都可以在 SQLFiddle 获得,供那些想要玩的人使用。
在我看来,针对此类问题的最佳解决方案是将每个时间戳截断到其 10 分钟的开头,例如,让我们尝试进行以下转换(original -> 10 minutes truncated
):
13:10 -> 13:10
13:15 -> 13:10
13:18 -> 13:10
13:20 -> 13:20
...
如果有人想尝试以下查询,您可以将架构创建为:
CREATE TABLE your_table(tscol timestamptz);
INSERT INTO your_table VALUES
('2011/01/03 19:18:00.300'),
('2011/01/03 19:18:00.503'),
('2011/01/03 19:20:26.335'),
('2011/01/03 19:29:54.289'),
('2011/01/04 14:43:43.067'),
('2011/01/04 14:50:10.727'),
('2011/01/04 14:52:26.827'),
('2011/01/04 14:57:55.608'),
('2011/01/04 14:57:55.718'),
('2011/01/04 14:59:13.603'),
('2011/01/04 15:00:34.260'),
('2011/01/04 15:02:55.687'),
('2011/01/04 15:07:15.378');
所以,为了做到这一点,我们需要了解date_trunc
函数date_part
(后者可以被标准调用EXTRACT
)和interval
数据类型。让我们一步一步构建解决方案,最终的想法是有这样的东西(现在是伪代码):
SELECT truncate_the_time_by_10_minutes(tscol) AS trunc10, count(*)
FROM your_table
GROUP BY trunc10
ORDER BY trunc10;
现在,如果问题是“按分钟聚合”,那么我们可以简单地将时间戳截断到分钟,这意味着将秒和微秒归零,这正是这样date_trunc('minute', ...)
做的,所以:
SELECT date_trunc('minute', tscol) AS trunc_minute, count(*)
FROM your_table
GROUP BY trunc_minute
ORDER BY trunc_minute;
工作,但它不是你想要的,下一个功能date_trun
是 with 'hour'
,它已经失去了我们需要的信息,所以我们需要介于'minute'
and之间的东西'hour'
。让我们看看上面的查询是如何与一些例子一起工作的:
SELECT tscol, date_trunc('minute', tscol) AS trunc_minute
FROM your_table
ORDER BY tscol;
返回:
tscol | trunc_minute
----------------------------+------------------------
2011-01-03 19:18:00.3-02 | 2011-01-03 19:18:00-02
2011-01-03 19:18:00.503-02 | 2011-01-03 19:18:00-02
2011-01-03 19:20:26.335-02 | 2011-01-03 19:20:00-02
2011-01-03 19:29:54.289-02 | 2011-01-03 19:29:00-02
...
如果你看到2011-01-03 19:18:00-02
,现在我们只需要减去 8 分钟,我们可以:
EXTRACT(MINUTE FROM tscol)
将返回18
- 因为我们想要截断 10 分钟,让我们取 的模
18 and 10
,所以18 % 10
这给了我们8
- 现在,我们有了
8
要减去的分钟数,但是作为整数,要减去timestamp[tz]
我们需要一个interval
,因为整数代表分钟,我们可以这样做:8 * interval '1 minute'
,这将给我们00:08:00
在最后一个查询中获得上述 3 个步骤,我们有(我将展示每一列以便更好地理解):
SELECT
tscol,
date_trunc('minute', tscol) AS trunc_minute,
CAST(EXTRACT(MINUTE FROM tscol) AS integer) % 10 AS min_to_subtract,
(CAST(EXTRACT(MINUTE FROM tscol) AS integer) % 10) * interval '1 minute' AS interval_to_subtract,
date_trunc('minute', tscol) - (CAST(EXTRACT(MINUTE FROM tscol) AS integer) % 10) * interval '1 minute' AS solution
FROM your_table
ORDER BY tscol;
返回:
tscol | trunc_minute | min_to_subtract | interval_to_subtract | solution
----------------------------+------------------------+-----------------+----------------------+------------------------
2011-01-03 19:18:00.3-02 | 2011-01-03 19:18:00-02 | 8 | 00:08:00 | 2011-01-03 19:10:00-02
2011-01-03 19:18:00.503-02 | 2011-01-03 19:18:00-02 | 8 | 00:08:00 | 2011-01-03 19:10:00-02
2011-01-03 19:20:26.335-02 | 2011-01-03 19:20:00-02 | 0 | 00:00:00 | 2011-01-03 19:20:00-02
2011-01-03 19:29:54.289-02 | 2011-01-03 19:29:00-02 | 9 | 00:09:00 | 2011-01-03 19:20:00-02
...
现在,最后一列是我们想要的解决方案,时间戳被截断为 10 分钟组,现在我们可以简单地聚合并得到最终解决方案:
SELECT
date_trunc('minute', tscol) - (CAST(EXTRACT(MINUTE FROM tscol) AS integer) % 10) * interval '1 minute' AS trunc_10_minute,
count(*)
FROM your_table
GROUP BY trunc_10_minute
ORDER BY trunc_10_minute;
返回:
trunc_10_minute | count
------------------------+-------
2011-01-03 19:10:00-02 | 2
2011-01-03 19:20:00-02 | 2
2011-01-04 14:40:00-02 | 1
2011-01-04 14:50:00-02 | 5
2011-01-04 15:00:00-02 | 5
(5 rows)
那是您给出的确切输出,但我相信这是您实际期望的,如果不是,这只是一个小的调整问题。