mysql - 使用 MySQL 聚合重叠事件

标签 mysql sql events aggregate overlapping

假设我们有看起来像的数据:

drop table if exists views; 
create table views(id int primary key,start time,end time); 
insert into views values 
(1, '15:01', '15:04'), 
(2, '15:02', '15:09'), 
(3, '15:12', '15:15'), 
(4, '16:11', '16:23'), 
(5, '16:19', '16:25'), 
(6, '17:52', '17:59'), 
(7, '18:18', '18:22'), 
(8, '16:20', '16:22'), 
(9, '18:17', '18:23'); 

像这样很容易想象

1     |-----| 
2        |-----| 
3                 |--| 
4                       |-----| 
5                          |-----| 
6                                  |---| 
7                                        |---|  
8                           |---| 
9                                       |-----| 

现在我想绘制数据图表,看起来像这样

+---------------------------+
|              x            |
|    x        x xxx     xxx |
|   x xx  xx x     xx  x    |
+---------------------------+

本质上是将它们分成 X 长度的片段,并计算每个 X 长度片段被触摸的次数。关于如何创建此 View 有任何想法吗?

(如果您必须知道这一点,那么我可以为视频分析创建参与数据)

我不希望输出为 ASCII,我希望它最终作为 SQL 中的查询结果。像这样的东西:

Time Start, Time End,  Num_Views
00:00, 00:05, 10
00:06, 00:10, 3
00:11, 00:15, 2
00:16, 00:20, 8

最佳答案

使用辅助数字表,你可以做这样的事情:

select
  r.Time_Start,
  r.Time_End,
  sum(v.id is not null) as Num_Views
from (
  select
    cast(from_unixtime((m.minstart + n.n + 0) * 300) as time) as Time_Start,
    cast(from_unixtime((m.minstart + n.n + 1) * 300) as time) as Time_End
  from (
    select
      unix_timestamp(date_format(minstart, '1970-01-01 %T')) div 300 as minstart,
      unix_timestamp(date_format(maxend  , '1970-01-01 %T')) div 300 as maxend
    from (
      select
        min(start) as minstart,
        max(end  ) as maxend
      from views
    ) s
  ) m
    cross join numbers n
  where n.n between 0 and m.maxend - minstart
) r
  left join views v on v.start < r.Time_End and v.end > r.Time_Start
group by
  r.Time_Start,
  r.Time_End
;

对于您的特定示例,此脚本产生以下输出:

Time_Start  Time_End  Num_Views
----------  --------  ---------
15:00:00    15:05:00  2
15:05:00    15:10:00  1
15:10:00    15:15:00  1
15:15:00    15:20:00  0
15:20:00    15:25:00  0
15:25:00    15:30:00  0
15:30:00    15:35:00  0
15:35:00    15:40:00  0
15:40:00    15:45:00  0
15:45:00    15:50:00  0
15:50:00    15:55:00  0
15:55:00    16:00:00  0
16:00:00    16:05:00  0
16:05:00    16:10:00  0
16:10:00    16:15:00  1
16:15:00    16:20:00  2
16:20:00    16:25:00  3
16:25:00    16:30:00  0
16:30:00    16:35:00  0
16:35:00    16:40:00  0
16:40:00    16:45:00  0
16:45:00    16:50:00  0
16:50:00    16:55:00  0
16:55:00    17:00:00  0
17:00:00    17:05:00  0
17:05:00    17:10:00  0
17:10:00    17:15:00  0
17:15:00    17:20:00  0
17:20:00    17:25:00  0
17:25:00    17:30:00  0
17:30:00    17:35:00  0
17:35:00    17:40:00  0
17:40:00    17:45:00  0
17:45:00    17:50:00  0
17:50:00    17:55:00  1
17:55:00    18:00:00  1
18:00:00    18:05:00  0
18:05:00    18:10:00  0
18:10:00    18:15:00  0
18:15:00    18:20:00  2
18:20:00    18:25:00  2

数字表可以是临时表,但我建议您创建并初始化一个永久表,因为它可用于多种用途。这是初始化数字表的一种方法:

create table numbers (n int);
insert into numbers (n) select 0;
insert into numbers (n) select cnt + n from numbers, (select count(*) as cnt from numbers) s;
insert into numbers (n) select cnt + n from numbers, (select count(*) as cnt from numbers) s;
insert into numbers (n) select cnt + n from numbers, (select count(*) as cnt from numbers) s;
insert into numbers (n) select cnt + n from numbers, (select count(*) as cnt from numbers) s;
insert into numbers (n) select cnt + n from numbers, (select count(*) as cnt from numbers) s;
insert into numbers (n) select cnt + n from numbers, (select count(*) as cnt from numbers) s;
insert into numbers (n) select cnt + n from numbers, (select count(*) as cnt from numbers) s;
insert into numbers (n) select cnt + n from numbers, (select count(*) as cnt from numbers) s;
/* repeat as necessary; every repeated line doubles the number of rows */

可以找到此脚本的“实时”版本on SQL Fiddle .

UPDATE(尝试描述所使用的方法)

上述解决方案实现了以下步骤:

  1. views表中找到最早的start时间和最晚的end时间。

  2. 将两个值都转换为 unix timestamps .

  3. 将两个时间戳除以 300,这实际上为我们提供了相应 5 分钟范围的索引(自大纪元以来)。

  4. 在数字表的帮助下,生成一系列 5 分钟的范围,涵盖 startend 之间的整个范围。

  5. 将范围列表与 views 表中的事件时间进行匹配(使用外部联接,因为我们想要(如果我们想要)考虑所有范围)。

  6. 按范围界限对结果进行分组并计算组中的事件数。 (而且我刚刚注意到上面查询中的 sum(v.id is not null) 可以替换为更简洁,在本例中更自然的 count(v .id).)

关于mysql - 使用 MySQL 聚合重叠事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10344191/

相关文章:

mysql - MySQL 的 `ORDER BY` 在多重集上是否不同?

mysql:重命名连接的所有字段

MySQL统计喜欢和不喜欢的数据

php - ZF2 : How to attach module specific listener for dispatch. 错误

mysql - 无法使用 DBI 将记录插入 MySQL 数据库

mysql - 是否可以在 linux 中将 mysql 表名从小写转换为大写?

javascript - 让事件处理程序忽略子项

javascript - angularjs 在事件内调用 Controller 的函数

mysql - 将 csv 文件加载到 MySQL,字段中以逗号分隔和逗号分隔

sql - 在 SQL 中使用记录作为表名