在一个时间范围内分组为 5 分钟的间隔

Posted

技术标签:

【中文标题】在一个时间范围内分组为 5 分钟的间隔【英文标题】:Grouping into interval of 5 minutes within a time range 【发布时间】:2011-05-19 13:08:15 【问题描述】:

我对想要执行的 mysql 命令有一些困难。

SELECT a.timestamp, name, count(b.name) 
FROM time a, id b 
WHERE a.user = b.user
  AND a.id = b.id
  AND b.name = 'John'
  AND a.timestamp BETWEEN '2010-11-16 10:30:00' AND '2010-11-16 11:00:00' 
GROUP BY a.timestamp

这是我当前的输出语句。

timestamp            name  count(b.name)
-------------------  ----  -------------
2010-11-16 10:32:22  John  2
2010-11-16 10:35:12  John  7
2010-11-16 10:36:34  John  1
2010-11-16 10:37:45  John  2
2010-11-16 10:48:26  John  8
2010-11-16 10:55:00  John  9
2010-11-16 10:58:08  John  2

如何将它们分组为 5 分钟间隔结果?

我希望我的输出是这样的

timestamp            name  count(b.name)
-------------------  ----  -------------
2010-11-16 10:30:00  John  2
2010-11-16 10:35:00  John  10
2010-11-16 10:40:00  John  0
2010-11-16 10:45:00  John  8
2010-11-16 10:50:00  John  0
2010-11-16 10:55:00  John  11 

【问题讨论】:

SELECT / GROUP BY - segments of time (10 seconds, 30 seconds, etc) 的可能重复项 【参考方案1】:

这适用于每个间隔。

PostgreSQL

SELECT
    TIMESTAMP WITH TIME ZONE 'epoch' +
    INTERVAL '1 second' * round(extract('epoch' from timestamp) / 300) * 300 as timestamp,
    name,
    count(b.name)
FROM time a, id 
WHERE …
GROUP BY 
round(extract('epoch' from timestamp) / 300), name

MySQL

SELECT
    timestamp,  -- not sure about that
    name,
    count(b.name)
FROM time a, id 
WHERE …
GROUP BY 
UNIX_TIMESTAMP(timestamp) DIV 300, name

【讨论】:

哦……没有得到 mysql-flag.. 这是一个 postgresql-query .. 但基本上这也应该用 mysql 来实现 ok .. 而不是 extract .. GROUP BY round(UNIX_TIMESTAMP(timestamp) / 300) 应该可以解决问题 @pHiL 的评论在 mySql 上是正确的,你应该使用 DIV 而不是 round(/) 否则间隔之间的边界是错误的 刚刚尝试了几个数据集,第二个查询非常适合 MySQL,这是 OP 关心的问题。既然@sky 似乎不存在,我们能否就这个答案达成集体共识? 我也试过这个。每隔 2 分钟或 3 分钟间隔,然后每隔 5 分钟,它就会显示第一个记录错误。注意:- 我添加了一个条件来获取最近 15 分钟的记录。【参考方案2】:

我遇到了同样的问题。

我发现很容易按任何分钟间隔进行分组 只需将 epoch 除以分钟(以秒为单位),然后四舍五入或使用 floor 来获得余数。因此,如果您想在 5 分钟 内获得间隔,您将使用 300 秒

    SELECT COUNT(*) cnt, 
    to_timestamp(floor((extract('epoch' from timestamp_column) / 300 )) * 300) 
    AT TIME ZONE 'UTC' as interval_alias
    FROM TABLE_NAME GROUP BY interval_alias
interval_alias       cnt
-------------------  ----  
2010-11-16 10:30:00  2
2010-11-16 10:35:00  10
2010-11-16 10:45:00  8
2010-11-16 10:55:00  11 

这将按选定的分钟间隔正确返回数据组;但是,它不会返回不包含任何数据的区间。为了得到这些空区间,我们可以使用函数generate_series。

    SELECT generate_series(MIN(date_trunc('hour',timestamp_column)),
    max(date_trunc('minute',timestamp_column)),'5m') as interval_alias FROM 
    TABLE_NAME

结果:

interval_alias       
-------------------    
2010-11-16 10:30:00  
2010-11-16 10:35:00
2010-11-16 10:40:00   
2010-11-16 10:45:00
2010-11-16 10:50:00   
2010-11-16 10:55:00   

现在要获得间隔为零的结果,我们只需外部连接两个结果集

    SELECT series.minute as interval,  coalesce(cnt.amnt,0) as count from 
       (
       SELECT count(*) amnt,
       to_timestamp(floor((extract('epoch' from timestamp_column) / 300 )) * 300)
       AT TIME ZONE 'UTC' as interval_alias
       from TABLE_NAME  group by interval_alias
       ) cnt
    
    RIGHT JOIN 
       (    
       SELECT generate_series(min(date_trunc('hour',timestamp_column)),
       max(date_trunc('minute',timestamp_column)),'5m') as minute from TABLE_NAME 
       ) series
  on series.minute = cnt.interval_alias

最终结果将包括所有 5 分钟间隔的系列,即使是那些没有值的系列。

interval             count
-------------------  ----  
2010-11-16 10:30:00  2
2010-11-16 10:35:00  10
2010-11-16 10:40:00  0
2010-11-16 10:45:00  8
2010-11-16 10:50:00  0 
2010-11-16 10:55:00  11 

可以通过调整generate_series的最后一个参数轻松更改间隔。在我们的例子中,我们使用 '5m',但它可以是 任何我们想要的间隔

【讨论】:

如果是 MySQL。似乎 generate_series 是一个 PostgreSQL 函数。太糟糕了。 第一个只给出当前数据结果的查询,它计算两个时间段中2个时间段的中间记录。就像在 10:35 和 10:40 这两个时间段中一样,这两组中的 10:40 都是 10:35 到 10:40 和 10:40 到 10:45 中的一个。【参考方案3】:

您应该使用GROUP BY UNIX_TIMESTAMP(time_stamp) DIV 300 而不是round(../300) 因为四舍五入我发现一些记录被计入两个分组的结果集中。

【讨论】:

这是正确的,round(../300) 在 mySql 上没有正确完成 对于那些好奇的人来说,MySQL 中的DIV 是一个浮点除法的floor(),对于BIGINTs 来说是安全的。 我也试过这个。每隔 2 分钟或 3 分钟间隔,然后每隔 5 分钟,它就会显示第一个记录错误。注意:- 我添加了一个条件来获取最近 15 分钟的记录。 应该使用 TRUNCATE 或 FLOOR 而不是 ROUND,因为舍入行为没有很好地定义并且取决于使用的 C 库。 lists.mysql.com/mysql/93613【参考方案4】:

对于postgres,我发现使用

date_trunc

函数,如:

select name, sum(count), date_trunc('minute',timestamp) as timestamp
FROM table
WHERE xxx
GROUP BY name,date_trunc('minute',timestamp)
ORDER BY timestamp

您可以为 date_trunc 提供各种分辨率,例如“分钟”、“小时”、“天”等。

【讨论】:

@tmarthal - 它不应该被赞成。最初的问题是针对 mysql 的。 你在哪里设置5 5 分钟的间隔? 对于上述情况,将 WHERE 子句更改为:WHERE timestamp > current_timestamp - interval '5 minutes' 这个查询似乎没有做所要求的,问题是'每 5' 分钟而不是 5 分钟之前。答案适合被否决【参考方案5】:

查询将类似于:

SELECT 
  DATE_FORMAT(
    MIN(timestamp),
    '%d/%m/%Y %H:%i:00'
  ) AS tmstamp,
  name,
  COUNT(id) AS cnt 
FROM
  table
GROUP BY ROUND(UNIX_TIMESTAMP(timestamp) / 300), name

【讨论】:

【参考方案6】:

不确定你是否还需要它。

SELECT FROM_UNIXTIME(FLOOR((UNIX_TIMESTAMP(timestamp))/300)*300) AS t,timestamp,count(1) as c from users GROUP BY t ORDER BY t;

2016-10-29 19:35:00 | 2016-10-29 19:35:50 | 4 |

2016-10-29 19:40:00 | 2016-10-29 19:40:37 | 5 |

2016-10-29 19:45:00 | 2016-10-29 19:45:09 | 6 |

2016-10-29 19:50:00 | 2016-10-29 19:51:14 | 4 |

2016-10-29 19:55:00 | 2016-10-29 19:56:17 | 1 |

【讨论】:

【参考方案7】:

您可能必须将您的时间戳分解为 ymd:HM 并使用 DIV 5 将分钟分成 5 分钟的箱子——类似于

select year(a.timestamp), 
       month(a.timestamp), 
       hour(a.timestamp), 
       minute(a.timestamp) DIV 5,
       name, 
       count(b.name)
FROM time a, id b
WHERE a.user = b.user AND a.id = b.id AND b.name = 'John' 
      AND a.timestamp BETWEEN '2010-11-16 10:30:00' AND '2010-11-16 11:00:00'
GROUP BY year(a.timestamp), 
       month(a.timestamp), 
       hour(a.timestamp), 
       minute(a.timestamp) DIV 12

...然后让客户端代码中的输出以您喜欢的方式出现。或者,如果您愿意,您可以使用 sql concat 运算符而不是获取单独的列来构建整个日期字符串。

select concat(year(a.timestamp), "-", month(a.timestamp), "-" ,day(a.timestamp), 
       " " , lpad(hour(a.timestamp),2,'0'), ":", 
       lpad((minute(a.timestamp) DIV 5) * 5, 2, '0'))

...然后分组

【讨论】:

嗯...但是输出没有得到我想要得到的东西。它返回一列,我不太确定计数的值是什么......【参考方案8】:

这个怎么样:

select 
    from_unixtime(unix_timestamp(timestamp) - unix_timestamp(timestamp) mod 300) as ts,  
    sum(value)
from group_interval 
group by ts 
order by ts
;

【讨论】:

【参考方案9】:

我发现使用 MySQL 可能正确的查询如下:

SELECT SUBSTRING( FROM_UNIXTIME( CEILING( timestamp /300 ) *300,  
                                 '%Y-%m-%d %H:%i:%S' ) , 1, 19 ) AS ts_CEILING,
SUM(value)
FROM group_interval
GROUP BY SUBSTRING( FROM_UNIXTIME( CEILING( timestamp /300 ) *300,  
                                   '%Y-%m-%d %H:%i:%S' ) , 1, 19 )
ORDER BY SUBSTRING( FROM_UNIXTIME( CEILING( timestamp /300 ) *300,  
                                   '%Y-%m-%d %H:%i:%S' ) , 1, 19 ) DESC

让我知道你的想法。

【讨论】:

【参考方案10】:
select 
CONCAT(CAST(CREATEDATE AS DATE),' ',datepart(hour,createdate),':',ROUNd(CAST((CAST((CAST(DATEPART(MINUTE,CREATEDATE) AS DECIMAL (18,4)))/5 AS INT)) AS DECIMAL (18,4))/12*60,2)) AS '5MINDATE'
,count(something)
from TABLE
group by CONCAT(CAST(CREATEDATE AS DATE),' ',datepart(hour,createdate),':',ROUNd(CAST((CAST((CAST(DATEPART(MINUTE,CREATEDATE) AS DECIMAL (18,4)))/5 AS INT)) AS DECIMAL (18,4))/12*60,2))

【讨论】:

请解释您的查询。【参考方案11】:

这将完全满足您的需求

替换 dt - 你的日期时间 c - 调用字段 astro_transit1 - 你的桌子 300 指 5 分钟,所以每次增加 300 以增加时间间隔

SELECT FROM_UNIXTIME( 300 * ROUND( UNIX_TIMESTAMP( r.dt ) /300 ) ) AS 5datetime, (
SELECT r.c
FROM astro_transit1 ra
WHERE ra.dt = r.dt
ORDER BY ra.dt DESC
LIMIT 1
) AS first_val FROM astro_transit1 r GROUP BY UNIX_TIMESTAMP( r.dt )
DIV 300
LIMIT 0 , 30

【讨论】:

以上是关于在一个时间范围内分组为 5 分钟的间隔的主要内容,如果未能解决你的问题,请参考以下文章

计算时间范围内的时间分组间隔

SQL如何在时间段内分组

pandas TimeGrouper 自定义频率时间范围

获取多个小时内按分钟时间范围内的 mySQL 行

如何将时间列分别分组为 5 分钟间隔和最大值/最小值 SQL?

以 5 分钟为间隔对 DataFrame 进行分组