Оптимизировать MySQL запрос с группой по - PullRequest
0 голосов
/ 11 мая 2019

У меня есть таблица InnoDB с 11 столбцами и около 5 миллионов записей, в которых я использую запрос, чтобы найти 10 лучших записей с наибольшей суммой.Схема таблицы выглядит следующим образом.

id (int 11) (primary key)
activity_id(varchar 250)
activity_type (varchar 10)
advertised_time (timestamp)
advertised_train_ident(int 11)
technical_train_ident(int 11)
location_signature(varchar 10)
time_at_location(timestamp)
information_owner(varchar 100)
created_at(timestamp)
updated_at(timestamp)

Индексы, представленные в таблице:

id - primary key
location_signature,activity_type, advertised_time - composite index (name is search)

Я использую следующий запрос для извлечения записей из вышеуказанной таблицы, и это занимает 10-12 секунд для завершения выполнения.

SELECT location_signature, activity_type,  
SUM(CASE WHEN TIMESTAMPDIFF(MINUTE,advertised_time, time_at_location) > 0 THEN TIMESTAMPDIFF(MINUTE,advertised_time, time_at_location) else 0 END) as delay_time, 
count(id) as total_train_count, 
SUM(CASE WHEN TIMESTAMPDIFF(MINUTE,advertised_time, time_at_location) > 0 THEN 1 ELSE 0 END) as delayed_train_count 
from `train_announcements` 
where `advertised_time` >= '2019-04-01 10:00:00' and `advertised_time` <= '2019-04-30 10:00:00' 
group by `location_signature`, `activity_type` 
order by `delay_time` desc 
limit 10 offset 0;

Оператор объяснения этого запроса имеет следующий вид:

+----+-------------+----------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
| id | select_type | table                      | type  | possible_keys | key     | key_len | ref  | rows   | Extra                                        |
+----+-------------+----------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
|  1 | SIMPLE      | train_announcements        | index | search        | search  | 84      | NULL | 4910024| Using where; Using temporary; Using filesort |
+----+-------------+----------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+

Обратите внимание, что в результате сопоставления этой таблицы utf8mb4_unicode_ci из-за поляlocation_signature содержит специальные символы.

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

Ответы [ 2 ]

3 голосов
/ 11 мая 2019

Обращаясь к своему индексу, убедитесь, что вы указали в поле рекламодателя время вверху слева

и может быть полезным, добавьте также врага time_at_location, чтобы избежать доступа к таблице данных и использовать данные из индекса

индекс для таблицы train_announcements

столбцы (advertised_time, location_signature, activity_type, time_at_location)

SELECT location_signature
  , activity_type
  , SUM(CASE WHEN TIMESTAMPDIFF(MINUTE,advertised_time, time_at_location) > 0 
            THEN TIMESTAMPDIFF(MINUTE,advertised_time, time_at_location) 
            ELSE 0 END) as delay_time
  , count(id) as total_train_count
  , SUM(CASE WHEN TIMESTAMPDIFF(MINUTE,advertised_time, time_at_location) > 0 
            THEN 1 
            ELSE 0 END) as delayed_train_count 
from `train_announcements` 
where `advertised_time` BETWEEN '2019-04-01 10:00:00' and '2019-04-30 10:00:00' 
group by `location_signature`, `activity_type` 
order by `delay_time` desc 
limit 10 offset 0;

и если у вас нет id с нулевым значением, попробуйте использовать count (*) вместо count (id)

SELECT location_signature
  , activity_type
  , SUM(CASE WHEN TIMESTAMPDIFF(MINUTE,advertised_time, time_at_location) > 0 
            THEN TIMESTAMPDIFF(MINUTE,advertised_time, time_at_location) 
            ELSE 0 END) as delay_time
  , count(*) as total_train_count
  , SUM(CASE WHEN TIMESTAMPDIFF(MINUTE,advertised_time, time_at_location) > 0 
            THEN 1 
            ELSE 0 END) as delayed_train_count 
from `train_announcements` 
where `advertised_time` BETWEEN '2019-04-01 10:00:00' and '2019-04-30 10:00:00' 
group by `location_signature`, `activity_type` 
order by `delay_time` desc 
limit 10 offset 0;

или если вам действительно нужен идентификатор, попробуйте добавить этот столбец в составной индекс

      (advertised_time, location_signature, activity_type, time_at_location, id )
0 голосов
/ 13 мая 2019

Создание и ведение сводной таблицы. Например, промежуточные итоги для каждого дня. Тогда «отчет» будет против этой намного меньшей таблицы, следовательно, будет намного быстрее.

Подробнее: http://mysql.rjweb.org/doc.php/summarytables

...