我有网络电台(MySQL)的统计表,有这样的列:
我需要选择每天的听众峰值,我的意思是同时发送的唯一IP侦听器的最大数量。
此高峰的开始和结束时间也很棒。
例如:
2011-30-01 | 4 listeners peak | from 10:30 | till 11:25
答案 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_start
和time_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
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的下一次出现,那么它就是一个峰值。 (峰值=立即开始,然后停止)