8

以前有人问这个问题,但我面临的问题略有不同。

我有一个记录事件并存储它们的时间戳(作为日期时间)的表。我需要能够将时间分成几块并获取在该时间间隔内发生的事件数。间隔可以自定义(比如从 5 分钟到 1 小时甚至更长)。

显而易见的解决方案是将 datetime 转换为 unix_timestamp 将其除以间隔中的秒数,取其 floor 函数并将其乘以秒数。最后将 unix_timestamp 转换回日期时间格式。

这适用于小间隔。

select 
from_unixtime(floor(unix_timestamp(event.timestamp)/300)*300) as start_time,
count(*) as total 
from event 
where timestamp>='2012-08-03 00:00:00' 
group by start_time;

这给出了正确的输出

+---------------------+-------+
| start_time          | total |
+---------------------+-------+
| 2012-08-03 00:00:00 |    11 |
| 2012-08-03 00:05:00 |     4 |
| 2012-08-03 00:10:00 |     4 |
| 2012-08-03 00:15:00 |     7 |
| 2012-08-03 00:20:00 |     8 |
| 2012-08-03 00:25:00 |     1 |
| 2012-08-03 00:30:00 |     1 |
| 2012-08-03 00:35:00 |     3 |
| 2012-08-03 00:40:00 |     3 |
| 2012-08-03 00:45:00 |     5 |
~~~~~OUTPUT SNIPPED~~~~~~~~~~~~

但是如果我将间隔增加到 1 小时(3600 秒)

mysql> select from_unixtime(floor(unix_timestamp(event.timestamp)/3600)*3600) as start_time, count(*) as total from event where timestamp>='2012-08-03 00:00:00' group by start_time;
+---------------------+-------+
| start_time          | total |
+---------------------+-------+
| 2012-08-02 23:30:00 |    35 |
| 2012-08-03 00:30:00 |    30 |
| 2012-08-03 01:30:00 |    12 |
| 2012-08-03 02:30:00 |    18 |
| 2012-08-03 03:30:00 |    12 |
| 2012-08-03 04:30:00 |     4 |
| 2012-08-03 05:30:00 |     3 |
| 2012-08-03 06:30:00 |    13 |
| 2012-08-03 07:30:00 |   269 |
| 2012-08-03 08:30:00 |   681 |
| 2012-08-03 09:30:00 |  1523 |
| 2012-08-03 10:30:00 |   911 |
+---------------------+-------+

据我所知,未正确设置边界的原因是 unix_timestamp 会将时间从我的本地时区 (GMT + 0530) 转换为 UTC,然后输出数值。

所以像 2012-08-03 00:00:00 这样的值实际上是 2012-08-02 18:30:00。划分和使用 floor 会将分钟部分设置为 00。但是当我使用 from_unixtime 时,它​​会将其转换回 GMT + 0530,因此给我从 30 分钟开始的间隔。

无论时区如何,如何确保查询正常工作?我使用 MySQL 5.1.52,所以 to_seconds() 不可用

编辑: 无论时间间隔如何(可以是小时、分钟、天),查询也应该正确触发。一个通用的解决方案将不胜感激

4

2 回答 2

7

您可以使用TIMESTAMPDIFF按时间间隔分组:

对于指定的小时间隔,您可以使用:

SELECT   '2012-08-03 00:00:00' + 
         INTERVAL FLOOR(TIMESTAMPDIFF(HOUR, '2012-08-03 00:00:00', timestamp) / <n>) * <n> HOUR AS start_time,
         COUNT(*) AS total 
FROM     event 
WHERE    timestamp >= '2012-08-03 00:00:00'
GROUP BY start_time

2012-08-03 00:00:00用您的最小输入日期替换出现。

<n>是您指定的以小时为单位的时间间隔(2每小时、3每小时等),您可以在几分钟内执行相同的操作:

SELECT   '2012-08-03 00:00:00' + 
         INTERVAL FLOOR(TIMESTAMPDIFF(MINUTE, '2012-08-03 00:00:00', timestamp) / <n>) * <n> MINUTE AS start_time,
         COUNT(*) AS total 
FROM     event 
WHERE    timestamp >= '2012-08-03 00:00:00'
GROUP BY start_time

<n>您指定的时间间隔在哪里(以分钟为单位) (每45分钟、每分钟90等)。

确保您将最小输入日期(在此示例中2012-08-03 00:00:00)作为第二个参数传递给TIMESTAMPDIFF.


编辑:如果您不想担心在TIMESTAMPDIFF函数中选择哪个间隔单位,那么当然只需按秒计算间隔(300 = 5 分钟,3600 = 1 小时,7200 = 2 小时等)

SELECT   '2012-08-03 00:00:00' + 
         INTERVAL FLOOR(TIMESTAMPDIFF(SECOND, '2012-08-03 00:00:00', timestamp) / <n>) * <n> SECOND AS start_time,
         COUNT(*) AS total 
FROM     event 
WHERE    timestamp >= '2012-08-03 00:00:00'
GROUP BY start_time

EDIT2:要解决您关于减少声明中必须传入最小参数日期的区域数量的评论,您可以使用:

SELECT   b.mindate + 
         INTERVAL FLOOR(TIMESTAMPDIFF(SECOND, b.mindate, timestamp) / <n>) * <n> SECOND AS start_time,
         COUNT(*) AS total 
FROM     event 
JOIN     (SELECT '2012-08-03 00:00:00' AS mindate) b ON timestamp >= b.mindate
GROUP BY start_time

只需将您的最小日期时间参数传入一次连接子选择。

您甚至可以在连接子选择中为您的秒间隔(例如3600)创建第二列,并将该列命名为secinterval... 然后将<n>'s 更改为b.secinterval,因此您只需传入最小日期参数和间隔一次.


SQLFiddle 演示

于 2012-08-03T06:06:40.753 回答
1

更简单的方法是:

方法1

select date(timestamp) as date_timestamp, hour(timestamp) as hour_timestamp, count(*) as total 
from event
where timestamp>='2012-08-03 00:00:00' 
group by date_timestamp, hour_timestamp

如果您想使用原始方法。

方法2

select from_unixtime(floor(unix_timestamp(event.timestamp-1800)/3600)*3600+1800) as start_time, 
count(*) as total 
from event 
where timestamp>='2012-08-03 00:00:00' 
group by start_time;

编辑1

对于第一种方法,它还允许用户设置不同的间隔。例如,如果用户希望日志按 15 分钟分组,

select date(time) as date_timestamp, 
    hour(time) as hour_timestamp,  
    floor(minute(time) as minute_timestamp / 15) * 15 as minute_timestamp
    count(*) as total
from event
group by date_timestamp, hour_timestamp, minute_timestamp
于 2012-08-03T05:53:27.527 回答