SQL для периодов времени - PullRequest
       6

SQL для периодов времени

7 голосов
/ 30 января 2012

У меня есть таблица статистики для интернет-радио (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 ]

3 голосов
/ 31 января 2012

ИМХО, проще загрузить эти 35'000 строк в память, перечислить их и вести подсчет одновременного прослушивателя в данный момент.
Это будет проще, если вы загрузите строку в следующем формате:

IP, Time, flag_That_Indicate_StartOrStop_Listening_For_This_Given_IP

, так что вы сможете загружать данные, упорядоченные по времени, и вам просто нужно перечислить все строки, сохраняя список прослушиваемых IP.

В любом случае, как вы рассматриваете несколько подключений с одного и того же IP-адреса?
Может быть 10 разных прослушивателей за NAT, использующих один и тот же IP-адрес.

Обновление: вы не оченьнеобходимо изменить структуру БД, достаточно использовать другой 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, то кто-то начинает прослушивание -> Добавьте его IP в список слушателей и увеличьте число слушателей на 1
, если StartStop = 0, этокто-то, кто прекращает прослушивание -> удаляет его IP из списка слушателей и уменьшает число слушателей на 1

и в цикле перечисления проверяет, когда вы достигаете максимального числа одновременных слушателей

2 голосов
/ 31 января 2012

Позвольте найти алгоритм для получения результатов с наилучшей производительностью.

  • Время разделения : Время - это непрерывное измерение, нам нужно отметить несколько точек в качестве контрольной точки, куда пересчитывают слушателя. Как найти интервалы или при проверке общего радиослушателя. Я считаю, что лучшая стратегия состоит в том, чтобы получить разные 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)

чтобы избежать сканирования таблицы.

  • Подсчет пика слушателей : Соедините предыдущую таблицу со своей таблицей, чтобы пересчитать пик на каждой контрольной точке времени:

Это мой подход для подсчета количества слушателей по времени контрольной точки:

  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) <- (1) Здесь </p>

  • Поиск максимального пика : К сожалению, 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 для каждого дня, теперь вам нужно искать непрерывный check times с тем же максимальным_пиком. Также MySQL не имеет статистических функций, ни CTE. Я предлагаю вам, чтобы этот код был написан на уровне приложения. Но, если вы хотите сделать это в решении для базы данных, это способ ( предупреждение производительности убийца ):

Сначала расширьте представление 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) 

Ответственность

  • Это не проверено. Уверены, что это ошибки с псевдонимами или столбцами таблицы.
  • Последние шаги убийцы производительности . Возможно, кто-то может предложить лучший подход для этих шагов.

Кроме того, я предлагаю вам создать временные таблицы и материализовать каждое представление этого ответа. Это улучшит производительность, а также вы сможете узнать, сколько времени занимает каждый шаг.

1 голос
/ 01 февраля 2012

Это, по сути, реализация ответа, данного Максом выше.Для простоты я буду представлять каждый эпизод прослушивания в качестве времени начала и продолжительности в виде целочисленных значений (их можно изменить на фактические даты и время, а затем потребуется изменить запросы для использования арифметики даты.)

> 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 (т. Е. Объединить каждую строку с последующей).

0 голосов
/ 30 января 2012
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)

Для каждой записи присоединяйтесь ко всему, что перекрывается.

MIN () перекрывающихся записей time_end - это когда первый текущий слушатель прекращает слушать.

Если это время меньше следующего вхождения time_start, это пик. (Пик = начало сразу после остановки)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...