#mysql #query-optimization
#mysql #запрос-оптимизация
Вопрос:
В моей таблице базы данных «ringtune_history» содержится 40 тысяч записей, а в «ringtunes» — 4 тысячи записей, и обработка запроса занимает слишком много времени.
SELECT `ringtunes`.*,
count(case when ringtune_history.Action = 'Download' then ringtune_history.Action end) as Org_Downloads,
count(case when ringtune_history.Action = 'View' then ringtune_history.Action end) as Org_Views,
count(case when ringtune_history.Action = 'Play' then ringtune_history.Action end) as Total_Plays,
count(case when ringtune_history.Action = 'Like' then ringtune_history.Action end) as Total_Likes,
`categories`.`Name` as `Category_Name`
FROM `ringtunes`
LEFT JOIN `ringtune_history` ON `ringtune_history`.`Ringtune_Id` = `ringtunes`.`Id`
LEFT JOIN `categories` ON `categories`.`Id` = `ringtunes`.`Category`
WHERE `ringtunes`.`Deleted` = 0
AND `ringtunes`.`Status` = 1
AND `categories`.`Deleted` = 0
AND `categories`.`Status` = 1
GROUP BY `ringtunes`.`Id`
ORDER BY `ringtunes`.`Id` DESC
LIMIT 20
Комментарии:
1. Вам нужно добавить индексы в вашу таблицу, создать индекс для всех таблиц. Для создания индексов используйте столбцы, которые вы указываете в предложении WHERE.
2. Очень полезно, спасибо!
Ответ №1:
Ваш запрос в порядке! Проблема в самой таблице.
Обязательно добавьте индексы к столбцам, которые вы используете для поиска записей (например. ringtunes.Deleted = 0
, ваш индекс должен будет перейти к удаленному столбцу). То же самое касается других столбцов, которые вы проверяете на наличие значений.
Ответ №2:
Не используйте LEFT
, если вам не нужны нули, где отсутствует «правильная» таблица. Очевидно, что categories
этого не должно быть LEFT
. Неясно, ringtune_history
можно ли изменить на обычный JOIN
.
Необходимы индексы:
ringtune: (deleted, status, ID) or (status, deleted, ID)
ringtune_history: (Ringtune_Id, Action)
(Пожалуйста, предоставьте SHOW CREATE TABLE
на случай, если есть другие подсказки.)
Похоже, у вас есть шаблон «explode-implode» — сначала вы JOIN
используете несколько таблиц, что приводит к потенциально большему набору промежуточных строк. При этом эта таблица довольно громоздкая из-за большого количества столбцов. Затем вы сворачиваете все с помощью GROUP BY
. Если возможно, сначала найдите идентификаторы таблицы, которую вы в конечном итоге GROUP BY
получите. Затем JOIN
это в другие таблицы и (вероятно) обратно в исходную таблицу — для получения остальных столбцов.
Вы можете, по крайней мере, отложить просмотр ringtune_history
до тех пор, пока не найдете 20 нужных строк в двух других таблицах. Это ограничит количество поисковых запросов в нем только 20, а не «всеми».
Если deleted
и status
из двух столбцов всегда совпадают, то не проверяйте их categories
. (Это упростит достижение исправления explode-implode.) Если они обязательно независимы, то я полагаю, что для каждой таблицы было бы лучше, если бы вы действительно удалили нежелательные строки (или переместили их в таблицу архива). Текущее WHERE
предложение является сложным, поскольку оно фильтрует две разные таблицы.
(Не критично:) Это
count(case when ringtune_history.Action = 'View' then ringtune_history.Action end)
может быть сокращен до
SUM(ringtune_history.Action = 'View')