MySQL один запрос: найти все соединения, которые были активны в течение минуты, за каждую минуту в течение всего дня - PullRequest
0 голосов
/ 05 февраля 2020

Допустим, у меня есть таблица, заполненная каждым соединением, которое обращается к моему веб-сайту, включая следующую информацию:

  • connection_id (int)
  • start_datetime (datetime)
  • end_datetime (datetime)
  • device_type (int, идентификатор для доступа по мобильному телефону (1), p c (2), прочее (3), et c.)

Я хотел бы - в одном запросе - получить счет за каждую минуту дня всех соединений, которые начались до или в течение этой минуты И закончились в течение или после этой минуты. Так что любой, кто был активен в любое время в течение этой минуты. Я хочу посмотреть время начала 6 часов назад, чтобы не проверять всю таблицу. В течение одной минуты (2020-01-25 10:10:00) запрос будет выглядеть следующим образом:

SELECT SUM(CASE WHEN device_type=1 THEN 1 ELSE 0 END) AS Mobile, 
SUM(CASE WHEN device_type=2 THEN 1 ELSE 0 END) AS PC, 
SUM(CASE WHEN device_type=3 THEN 1 ELSE 0 END) AS Other FROM 
connection_table WHERE start_datetime BETWEEN '2020-01-25 04:10:00' AND '2020-01-25 10:10:59' 
AND end_datetime > '2020-01-25 10:10:00';

Один запрос работает нормально, поэтому я могу go каждую минуту дня и сделать этот запрос, но было бы здорово, если бы я мог сделать это в одном запросе вместо этого. Одиночный запрос, конечно, также должен возвращать столбец с той минутой, за которую он получил счет. У меня есть таблица с именем all_minutes с записью для каждой минуты следующих нескольких лет, потому что я где-то читал, что это обходной путь для получения возвращаемого значения для каждой минуты , даже если я не надену не обязательно иметь записи, соответствующие этой минуте в таблице . К этой таблице можно присоединиться, если это поможет.

Есть ли способ сделать это в одном запросе? Пример предпочтительного набора возвращаемых значений:

Datetime            | Mobile | PC  | Other
2020-01-25 00:00:00 | 30     | 120 | 17
2020-01-25 00:01:00 | 33     | 121 | 18
2020-01-25 00:02:00 | 38     | 122 | 18
2020-01-25 00:03:00 | 37     | 116 | 19
...
2020-01-25 23:59:00 | 30     | 120 | 17

Я пытался с подзапросом, но я не сделал его работать. Это была одна из моих попыток, но она теряет возможность использовать свой индекс, когда я пытаюсь передать ему all_minutes.the_datetime в качестве параметра "DT". Также я не могу вернуть 3 значения в подзапросе, и выполнение 3 отдельных подзапросов не является хорошим вариантом.

SELECT the_datetime as dt, (SELECT COUNT(*) FROM connection_table WHERE 
start_datetime BETWEEN DATE_SUB(dt, INTERVAL 6 HOUR) 
AND DATE_ADD(dt, INTERVAL 59 SECOND) AND end_datetime > dt) AS connections
FROM all_minutes WHERE the_datetime BETWEEN '2020-01-25 00:00:00' and '2020-01-25 23:59:59';

ОБНОВЛЕНИЕ: я также попытался, присоединившись к таблице all_minutes и к connection_table, как @ spencer7593 и @GMB предлагает , но это останавливает применение индекса моей таблицы соединений (start_datetime > end_datetime), заставляя его проверять каждую строку в таблице (и это очень большая таблица). FORCE INDEX тоже не работает.

1 Ответ

0 голосов
/ 05 февраля 2020

условие для совпадения ...

 WHERE t.start_datetime  >=  '2020-01-25 04:10:00'
   AND t.start_datetime   <  '2020-01-25 10:11:00' 
   AND t.end_datetime     >  '2020-01-25 10:10:00'

можем ли мы вывести эти литералы из одного значения? рассмотрим:

 SELECT '2020-01-25 10:10:00'  + INTERVAL -6 HOUR
      , '2020-01-25 10:10:00'  + INTERVAL 1 MINUTE
      , '2020-01-25 10:10:00'  + INTERVAL 0 MINUTE

, поэтому мы знаем, что это эквивалентно

 WHERE t.start_datetime  >=  '2020-01-25 10:10:00'  + INTERVAL -6 HOUR
   AND t.start_datetime   <  '2020-01-25 10:10:00'  + INTERVAL 1 MINUTE 
   AND t.end_datetime     >  '2020-01-25 10:10:00'  + INTERVAL 0 MINUTE

, теперь мы можем заменить этот литерал '2020-01-25 10:10' на выражение

мы могли бы сделать что-то вроде этого:

SELECT m.dt

     , SUM(CASE WHEN t.device_type=1 THEN 1 ELSE 0 END) AS Mobile
     , SUM(CASE WHEN t.device_type=2 THEN 1 ELSE 0 END) AS PC
     , SUM(CASE WHEN t.device_type=3 THEN 1 ELSE 0 END) AS Other 

  FROM all_minutes m

  LEFT
  JOIN connection_table t
    ON t.start_datetime  >=  m.dt  + INTERVAL -6 HOUR
   AND t.start_datetime   <  m.dt  + INTERVAL 1 MINUTE 
   AND t.end_datetime     >  m.dt  + INTERVAL 0 MINUTE

 WHERE m.dt >= '2020-01-25 10:00' 
   AND m.dt  < '2020-01-25 10:16'  

 GROUP 
    BY m.dt
...