【问题标题】:MySql query histogram for time intervals dataMySql查询时间间隔数据的直方图
【发布时间】:2015-11-09 05:01:09
【问题描述】:

我有一个这种类型的事件输入

event user
event start
event end
event type

插入到 MySql 表中,每个都在自己的行中,以 user+start 作为主键。

我需要按时间间隔(比如分钟)查询每个时间间隔上发生的计数事件的类型的直方图。 类似:

SELECT count(*) as hits FROM events 
WHERE type="browsing" 
GROUP BY time_diff("2015-1-1" AND "2015-1-2") / 60 * second

但是除了编写代码之外,我在 SQL 中找不到任何方法,知道吗?

样本数据

user, start, end, type
1, 2015-1-1 12:00:00, 2015-1-1 12:03:59, browsing
2, 2015-1-1 12:03:00, 2015-1-1 12:06:00, browsing
2, 2015-1-1 12:03:00, 2015-1-1 12:06:00, eating
3, 2015-1-1 12:03:00, 2015-1-1 12:08:00, browsing

结果应该是这样的:

         ^
count    |
browsing |
users    |       *
         |       *  *  *  *
         | *  *  *  *  *  *  *  *
         --|--|--|--|--|--|--|--|--|--> minute
         0  1  2  3  4  5  6  7  8  9 

【问题讨论】:

  • 提供样本数据和期望的结果。目前尚不清楚您在寻找什么。
  • 。 . 在问题中添加示例数据和所需结果。
  • 我添加了样本和所需结果
  • “除了写代码”是什么意思?你认为 SQL 语句是代码还是非代码?
  • @moshebeeri 。 . .这有两个具有挑战性的部分。一种是获取直方图(分钟列表)所需的所有时间单位,另一种是进行计算。你有第一个吗?

标签: mysql sql time-series


【解决方案1】:

您可以使用 group by 和您想要的级别来执行此操作。这是使用您提供的数据的示例:

首先使用 SQL 创建表并填充它。这里的 ID 列不是“必需的”,但如果表很大或上面有索引,建议使用。

CREATE TABLE `test`.`events` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `user` INT NULL,
  `start` DATETIME NULL,
  `end` DATETIME NULL,
  `type` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));

INSERT INTO events (user, start, end, type) VALUES 
(1, '2015-1-1 12:00:00', '2015-1-1 12:03:59', 'browsing'),
(2, '2015-1-1 12:03:00', '2015-1-1 12:06:00', 'browsing'),
(2, '2015-1-1 12:03:00', '2015-1-1 12:06:00', 'eating'),
(3, '2015-1-1 12:03:00', '2015-1-1 12:08:00', 'browsing');

要获得有序对的分钟数持续时间到事件数的列表:

然后可以使用 timestampdiff 函数轻松编写查询,如下所示:

SELECT 
    TIMESTAMPDIFF(MINUTE, start, end) as minutes,
    COUNT(*) AS numEvents
FROM
    test.events
GROUP BY TIMESTAMPDIFF(MINUTE, start, end)

输出:

minutes      numEvents
3            3
5            1

选择中的第一个参数可以是 FRAC_SECOND、SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER 或 YEAR 之一。

以下是您可以执行的更多查询示例:

按小时显示的事件(应用了地板功能)

SELECT 
    TIMESTAMPDIFF(HOUR, start, end) as hours,
    COUNT(*) AS numEvents
FROM
    test.events
GROUP BY TIMESTAMPDIFF(HOUR, start, end)

**按小时显示事件,格式更好**

SELECT 
    CONCAT("<", TIMESTAMPDIFF(HOUR, start, end) + 1) as hours,
    COUNT(*) AS numEvents
FROM
    test.events
GROUP BY TIMESTAMPDIFF(HOUR, start, end)

您可以按多种选项进行分组,但这绝对可以帮助您入门。大多数绘图包都允许您指定任意 x y 坐标,因此您无需担心 x 轴上的缺失值。

获取特定时间的有序事件对列表(用于记录): 请注意,这是留作参考。

现在进行查询。首先,您必须选择要用于分组的项目。例如,一项任务可能需要超过一分钟,因此开始和结束的时间可能不同。对于所有这些示例,我都基于开始时间,因为那是事件实际发生的时间。

要按分钟对事件计数进行分组,您可以使用如下查询:

SELECT 
     DATE_FORMAT(start, '%M %e, %Y %h:%i %p') as minute, 
     count(*) AS numEvents 
FROM test.events 
GROUP BY YEAR(start), MONTH(start), DAYOFMONTH(start), HOUR(start), MINUTE(start);

请注意这是如何按所有项目分组的,从年份开始,一直到分钟。我也将分钟显示为标签。结果输出如下所示:

minute                      numEvents
January 1, 2015 12:00 PM    1
January 1, 2015 12:03 PM    3

这是您可以使用 php 获取的数据,并准备将其显示在众多图形库之一中,在 x 轴上绘制分钟列,在 y 轴上绘制 numEvents。

以下是您可以执行的更多查询示例:

每小时的事件

SELECT 
     DATE_FORMAT(start, '%M %e, %Y %h %p') as hour, 
     count(*) AS numEvents 
FROM test.events 
GROUP BY YEAR(start), MONTH(start), DAYOFMONTH(start), HOUR(start);

事件按日期

SELECT 
    DATE_FORMAT(start, '%M %e, %Y') as date, 
    count(*) AS numEvents 
FROM test.events 
GROUP BY YEAR(start), MONTH(start), DAYOFMONTH(start);

每月活动

SELECT 
    DATE_FORMAT(start, '%M %Y') as date, 
    count(*) AS numEvents 
FROM test.events 
GROUP BY YEAR(start), MONTH(start);

每年的事件

SELECT 
    DATE_FORMAT(start, '%Y') as date, 
    count(*) AS numEvents 
FROM test.events 
GROUP BY YEAR(start);

我还应该指出,如果您在此表的起始列上有一个索引,这些查询将很快完成,即使有数亿行。

希望这会有所帮助!如果您对此有任何其他问题,请告诉我。

【讨论】:

  • 至于绘图部分,我会在这里留下一个附录。那里有很多不错的库,您可以查看这些库(通常,我们尝试将堆栈溢出的问题保留在帖子中的一个问题中,我们不推荐教程或服务,因为它们主要基于意见.) 但是,我在这里的查询产生的数据应该很容易在任何主要库中绘制。
  • 查看 D3 - d3js.org 或 ChartJS - chartjs.org 以获得几个示例。
  • 它的想法很酷,但它似乎不符合问题中的示例。例如,在第 4 分钟,有两个用户在浏览网页,而您的查询似乎错过了这一点。您的查询适用于按时间戳记的事件,我的问题是处理间隔
  • 啊,我误解了你的问题。我可能会更新我的答案,但我可能要几天才能得到它。
  • 嗨,如果你能/有更新,我会更高兴如果你能发布它。我相信这个问题提出了一个一般 SQL 没有很好地涵盖的点,因此这个领域的知识很幸运。
【解决方案2】:

我将假设您有一个包含整数的数字表。你还有$starttime$endtime

这是获取所需值的一种方法:

select ($starttime + interval n.n - 1 minute) as thetime, n.n as minutes,
       count(sd.user)
from numbers n left join
     sampledata sd
     on $starttime + interval n.n - 1 minute between sd.start and sd.end
where $starttime + interval n.n - 1 minute <= $endtime and
      sd.end >= $starttime and
      sd.start <= $endtime
group by n.n
order by n.n;

【讨论】:

  • 10x Gordon,似乎这个解决方案会起作用,基本思想是加入时间间隔表,其中间隔应该是事件时间之间的间隔。认为这个想法解决了我觉得它不是最有效的问题,也不是奥卡姆剃刀解决方案,特别是当给定一个以上的时间序列时,需要检查它们之间的后续事件发生(这是我正在处理的主要问题,例如比较或计算在听音乐时浏览网页的用户。)我希望 SQL 中有更好的方法来做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-25
  • 1970-01-01
  • 2021-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-04
相关资源
最近更新 更多