如何编写MySQL查询以根据时间范围获取计数?

时间:2012-03-05 06:32:01

标签: mysql sql mysqli aggregate-functions

我有这样一张桌子:

userId     loggedtime
-------------------
1        2012-03-05 10:45:59
2        2012-03-05 10:04:19
2        2012-03-05 10:05:09
4        2012-03-05 10:20:24
3        2012-03-05 10:20:20
6        2012-03-05 10:30:59
7        2012-03-05 10:35:59

我想选择在10点到11点之间每隔5分钟登录一次的用户数量。

我需要这样的结果。

time       count
---------------
10:05        2 
10:20        2 
10:30        1 
10:35        1

如何编写用于创建此类结果的MySQL查询?

5 个答案:

答案 0 :(得分:3)

你只需要弄清楚分组的独特之处,就会给你5分钟的间隔。

如果您按时间分,并将其除以5,那么您将拥有一些独特的东西。例如,您可以GROUP BY CAST(EXTRACT(MINUTE FROM logTime)/ 5 AS unsigned),这将使您的5分钟间隔唯一。 / 5将默认分割而不进行舍入。

要实际编写您的工作查询,请分阶段尝试。

首先,让我们得到一分钟:

mysql> SELECT userid, EXTRACT(MINUTE FROM loggedtime) as minute from test;
+--------+--------+
| userid | minute |
+--------+--------+
|      1 |     45 |
|      2 |      4 |
|      2 |      5 |
|      4 |     20 |
|      3 |     20 |
|      6 |     30 |
|      7 |     35 |
+--------+--------+
7 rows in set (0.00 sec)

现在,让我们尝试为每个可以分组的5分钟间隔创建一个唯一的列值。这是分钟除以5而没有舍入(上述值/ 5):

mysql> select userid, EXTRACT(MINUTE FROM loggedtime) AS minute, CAST(EXTRACT(MINUTE FROM loggedtime) / 5 AS unsigned) FROM test;


+--------+--------+-------------------------------------------------------+
| userid | minute | CAST(EXTRACT(MINUTE FROM loggedtime) / 5 AS unsigned) |
+--------+--------+-------------------------------------------------------+
|      1 |     45 |                                                     9 |
|      2 |      4 |                                                     1 |
|      2 |      5 |                                                     1 |
|      4 |     20 |                                                     4 |
|      3 |     20 |                                                     4 |
|      6 |     30 |                                                     6 |
|      7 |     35 |                                                     7 |
+--------+--------+-------------------------------------------------------+
7 rows in set (0.01 sec)

最后,我们按照该唯一列进行分组。此查询使用MIN()和MAX()来显示该时间间隔中的第一个和最后一个时间戳,但如果您希望它与您的问题完全相同,您还可以计算截止时间。

mysql> SELECT COUNT(*) AS user_count, MIN(loggedtime) AS first_time, 
MAX(loggedtime) AS last_time 
FROM test 
GROUP BY CAST(EXTRACT(MINUTE FROM loggedtime) / 5 AS unsigned) 
ORDER BY last_time;
+------------+---------------------+---------------------+
| user_count | first_time          | last_time           |
+------------+---------------------+---------------------+
|          2 | 2012-03-05 10:04:19 | 2012-03-05 10:05:09 |
|          2 | 2012-03-05 10:20:20 | 2012-03-05 10:20:24 |
|          1 | 2012-03-05 10:30:59 | 2012-03-05 10:30:59 |
|          1 | 2012-03-05 10:35:59 | 2012-03-05 10:35:59 |
|          1 | 2012-03-05 10:45:59 | 2012-03-05 10:45:59 |
+------------+---------------------+---------------------+
5 rows in set (0.00 sec)

答案 1 :(得分:2)

如果您要定期进行此分析,可能需要为您的时间间隔设置一个表格。从您的样本数据和所需结果来看,我创建了一个表:

Time_Intervals
I_Start      I_End
10:01:00     10:05:59
10:06:00     10:10:59
10:11:00     10:15:59
...
10:56:00     11:00:59

而且,实际上,它每天每小时将包含12个间隔(尽管23:56:00..00:00:59的回合将提供一些娱乐 - 它留给了一个练习读者!)。

然后,您只需使用此表格即可生成分组:

SELECT i.i_end, COUNT(*)
  FROM Time_Intervals AS i
  JOIN ThisKindOfATable AS t
    ON t.loggedtime BETWEEN i.i_start AND i.i_end
 WHERE t.loggedtime BETWEEN '2012-03-05 10:01:00' AND '2012-03-05 11:00:59'
 GROUP BY i.i_end
 ORDER BY i.i_end;

我使用了BETWEEN ... AND来加入。替代设计将使用范围10:01:00 .. 10:06:00和连接条件t.loggedtime >= i.i_start AND t.loggedtime < i.i_end;这会更好地处理小数秒。

您可能需要对loggedtime进行一些调整,将其从DATE + TIME转换为加入条件的TIME(可能是TIME(t.loggedtime))。您还可以对此进行调整,以使用LEFT JOIN而不是INNER JOIN为没有用户登录的区间打印零。您可能还决定放弃部分时间来简化操作。变化很大。

对此的主要替代方法是通过一些适当的计算将给定时间(t.loggedtime值)转换为区间数。这将进入高度DBMS特定的代码 - 操作时间的函数在DBMS之间基本上是不标准化的。

答案 2 :(得分:1)

如果你想让它显示用户分别登录的5分钟间隔,只需将(分钟/ 5)的INTEGER除以然后乘以5. Ex:10:03将向下舍入为0,乘以5将代表10:00。 10:05将四舍五入为1 * 5 = 10:05

select
      concat( '10:', LPAD( floor( minute( UL.loggedTime ) / 5 ) * 5, 2, '0' ) as MinuteRange,
      count(*) as LoginCount
   from
      UserLoginTable UL
   where
      UL.loggedTime between '2012-03-05 10:00:00' AND '2012-03-05 10:59:59' 
   group by 
      MinuteRange

是的,它的硬编码为例子,但你也可以替换这么多元素......从5分钟到10或15(或其他)的间隔,甚至格式化起始'10:'(小时表示)使用:

LPAD(小时(UL.LoggedTime),2,'0')

所以如果你做了更长的时间范围...比如21:00:00到23:59:59会显示范围内的所有小时。

请注意。由于您要与日期/时间列进行比较,任何想要仅使用“日期”部分的查询,请确保包含从00:00:00到11:59:59的整个日期范围,否则您可能会遗漏某些预期的数据

答案 3 :(得分:0)

我不知道你现在怎么做,我必须更多地考虑你需要的结果。但是你必须在Mysql中使用timediff函数

示例:

SELECT TIMEDIFF('2009-02-01 00:00:00', '2009-01-01 00:00:00');

绝对必须在MySQL查询中使用for循环来计算每5分钟所需的结果。

Look at this too.

答案 4 :(得分:0)

尝试此查询

SELECT
    loggedTime,  
    count(UserId)
FROM tableName
WHERE loggedTime BETWEEN '2010-11-16 10:00:00' AND '2010-11-16 11:00:00' 
GROUP BY 
round(UNIX_TIMESTAMP(timestamp) / 300), name