SQL用于时间段

时间:2012-01-30 14:52:24

标签: mysql sql

我有网络电台(MySQL)的统计表,有这样的列:

  • IP_ADDRESS
  • time_start(收听开始的日期时间)
  • time_end(听完的日期时间)

我需要选择每天的听众峰值,我的意思是同时发送的唯一IP侦听器的最大数量。

此高峰的开始和结束时间也很棒。

例如:

2011-30-01  |  4 listeners peak  |  from 10:30  |  till 11:25

enter image description here

4 个答案:

答案 0 :(得分:3)

恕我直言,在内存中加载这些35'000行更简单,枚举它们,并在给定时刻保持并发侦听器的计数。
如果以下列格式加载行,这将更简单:

IP, Time, flag_That_Indicate_StartOrStop_Listening_For_This_Given_IP

因此您将能够加载按时间排序的数据,并且您应该简单地枚举维护侦听IP列表的所有行。

无论如何,您如何考虑来自同一IP的多个连接? 使用相同的IP地址在NAT后面可以有10个不同的监听器。

更新: 您实际上并不需要更改数据库结构,只需使用不同的SQL来加载数据

SELECT ip_address, Time_Start AS MyTime, 1 As StartStop
FROM MyTable
ORDER BY Time_Start

UNION ALL

SELECT ip_address, Time_Stop AS MyTime, 0 As StartStop
FROM MyTable

使用此SQL,您应该能够加载所有数据,然后枚举所有行 正确排序行非常重要。

如果StartStop = 1,则开始收听的是somone - >将其IP添加到侦听器列表中,并将侦听器计数增加1 如果StartStop = 0则是某人停止收听 - >从侦听器列表中删除它的IP,并将侦听器的数量减少1

并在枚举循环中检查何时达到最大并发侦听器数

答案 1 :(得分:2)

让我们找一个算法来获得最佳性能的结果。

  • 分裂时间:时间是一个连续的维度,我们需要一些点来标记为检查点,侦听器在哪里重新计数。如何查找间隔或检查总收音机监听器。我认为最好的策略是获得不同的time_starttime_end

这是我分时的方法。我创建了一个简化帖子的视图:

create view time_split as
select p_time from (
  Select 
       time_start
  from 
       your_table
  union
  Select 
       time_end
  from 
       your_table
  ) as T

我建议你2个数据库索引:

your_table( time_start, time_end)  <--(1) explained below
your_table( time_end)

避免使用tablecan。

  • 计算侦听器达到峰值:使用您的表加入上一个表,以便在每次检查点重新计算峰值:

这是我按检查点时间计算听众的方法:

  create view peak_by_time as
  select p_time, count(*) as peak
  from
     your_table t
        inner join
     time_split
        on time_split.p_time between t.time_start and t.time_end
  group by
     p_time
  order by 
     p_time, peak

请记住在your_table上创建一个数据库索引(time_start,time_end)&lt; - (1)Here

  • 寻找最高峰值:不幸的是,MySQL没有分析功能,因此over partition不可用,并且无法在之前的视图中获取一天中的最大峰值。然后你应该获得以前观看的最大峰值。这是性能杀手操作。我建议您在应用程序逻辑中进行此操作,然后再进行数据库操作。

这是我按天获取max_peak的方法(性能杀手):

  create view max_peak_by_day as
  select 
       cast(p_time as date) as p_day ,
       max(peak) as max_peak
  from peak_by_time
  group by cast(p_time as date)
  • 寻找广告位时间:此时您每天都有max_peak,现在您需要查找具有相同max_peak的连续check times。 MySQL也没有CTE的统计功能。我建议你这个代码将在app层上编写。但是,如果你想在数据库解决方案中这样做,这是一种方式(警告性能杀手):

首先,展开peak_by_time视图以获取p_time和之前p_time的上一个峰值:

create view time_split_extended as
select c.p_time, max( p.p_time) as previous_ptime
from 
  time_split c
    inner join 
  time_split p
    on p.p_time < c.p_time
group by c.p_time

create view peak_by_time_and_previous as
select 
   te.p_time,  
   te.previous_ptime, 
   pc.peak as peak, 
   pp.peak as previous_peak
from 
  time_split_extended te
    inner join 
  peak_by_time pc on te.p_time = pc.p_time
    inner join
  peak_by_time pp on te.previous_ptime = pp.p_time

现在检查上一个插槽和当前插槽是否有max_peak:

select 
   cast(p_time as date) as p_day, 
   min( p_time ) as slot_from, 
   max( p_time) as slot_to, 
   peak
from 
   peak_by_time_and_previous p
      inner join 
   max_peak_by_day m
      on cast(p.p_time as date) = m.p_day and
         p.peak = m.max_peak
where 
   p.peak = p.previous_peak
group by  cast(p_time as date) 

<强>声明

  • 未经测试。当然,它们是表别名或列的错误。
  • 最后一步是性能杀手。也许有人可以为这些步骤提出最佳方法。

另外,我建议您创建临时表并实现此答案的每个视图。这样可以提高性能,也可以知道每个步骤需要多少时间。

答案 2 :(得分:1)

这实际上是Max上面给出的答案的实现。为简单起见,我将每个收听剧集表示为开始时间和长度作为整数值(它们可以更改为实际日期时间,然后需要修改查询以使用日期算法。)

> select * from episodes;
+--------+------+
| start  | len  |
+--------+------+
|  50621 |  480 |
|  24145 |  546 |
|  93943 |  361 |
|  67668 |  622 |
|  64681 |  328 |
| 110786 |  411 |
...

以下查询将开始和结束时间与UNION组合在一起,标记结束时间以区分开始时间,并保持运行累加器的侦听器数量:

SET @idx=0;
SET @n=0;
SELECT (@idx := @idx + 1) as idx,
       t,
       (@n := @n + delta) as n
  FROM
  (SELECT start AS t,
          1 AS delta
     FROM episodes
     UNION ALL
     SELECT start + len AS t,
            -1 AS delta FROM episodes
     ORDER BY t) stage

+------+--------+------+
| idx  | t      | n    |
+------+--------+------+
|    1 |      8 |    1 |
|    2 |    106 |    2 |
|    3 |    203 |    3 |
|    4 |    274 |    2 |
|    5 |    533 |    3 |
|    6 |    586 |    2 |
...

其中't'是每个间隔的开始(只要听众的数量“n”改变,它就是一个新的“间隔”)。在“t”是实际日期时间的版本中,您可以轻松地按天分组以获取每天的高峰情节或其他此类摘要。要获得每个间隔的结束时间 - 您可以使用上面的表格并将其连接到right.idx = left.idx + 1(即将每一行与后续行连接)。

答案 3 :(得分:0)

SELECT
  COUNT(*)               AS listeners,
  current.time_start,    AS peak_start,
  MIN(overlap.time_end)  AS peak_end
FROM
  yourTable    AS current
INNER JOIN
  yourTable    AS overlap
    ON  overlap.time_start <= current.time_start
    AND overlap.time_end   >  current.time_start
GROUP BY
  current.time_start,
  current.time_end
HAVING
  MIN(overlap.time_end) < COALESCE((SELECT MIN(time_start) FROM yourTable WHERE timeStart > current.timeStart), current.time_end+1)

对于每条记录,请加入重叠的所有内容。

重叠记录'time_end的MIN()是第一个当前监听器停止收听的时间。

如果该时间小于time_start的下一次出现,那么它就是一个峰值。 (峰值=立即开始,然后停止)