Мне нужно ускорить этот запрос для списка последних сообщений. Этот запрос выполняется слишком долго (например, 10 секунд …)
SELECT datas.uid,
datas.message,
datas.date,
CONCAT(conv.first_name, ' ', conv.last_name) AS conversation_name
FROM (SELECT m.message_id,
m.message,
IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
m.readed,
m.sended AS `date`
FROM users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
WHERE m.message_id IN (SELECT MAX(message_id)
FROM messages
WHERE to_uid = 1
OR from_uid = 1
GROUP BY LEAST(from_uid, to_uid),
GREATEST(from_uid, to_uid))) datas
LEFT JOIN users conv
ON conv.user_id = datas.uid
ORDER BY datas.date DESC
LIMIT 5
В этом запросе используются 2 таблицы (пользователи и сообщения).
Таблица пользователей:
Табличные сообщения:
РЕДАКТИРОВАТЬ
Я добавил индексы к сообщениям:
— from_uid
— to_uid
— отправил
и это без эффекта …
Попробуйте создать проиндексированный идентификатор, который вы проверяете.
В этом случае вы можете создать индекс для: conv.user_id, datas.uid, m.message_id, messages.to_uid, messages.from_uid и datas.date, что также может быть хорошей идеей, так как вы сортируете по тот.
Добавить индексы на from_uid
а также to_uid
ускорить SELECT MAX(message_id)
подзапрос. В противном случае он должен сделать полное сканирование таблицы.
Я хотел бы попытаться удалить подзапрос (ы)
Вы можете немного его очистить, дважды присоединяясь к пользователям, когда получаете данные пользователя от te и до uid. Таким образом, объединения могут эффективно использовать индексы. Затем просто используйте IF в SELECT, чтобы решить, какой из них вернуть: —
SELECT IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
m.message,
m.sended AS `date`,
IF (m.from_uid = 1, CONCAT(to_conv.first_name, ' ', to_conv.last_name), CONCAT(from_conv.first_name, ' ', from_conv.last_name)) AS conversation_name
FROM users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
LEFT JOIN users to_conv
ON to_conv.user_id = m.to_uid
LEFT JOIN users from_conv
ON from_conv.user_id = m.from_uid
WHERE m.message_id IN
(
SELECT MAX(message_id)
FROM messages
WHERE to_uid = 1
OR from_uid = 1
GROUP BY LEAST(from_uid, to_uid),
GREATEST(from_uid, to_uid)
)
ORDER BY date DESC
LIMIT 5
Подзапрос для проверки идентификатора сообщения удалить немного сложнее. Вообще IN работает плохо. Возможно, стоит изменить его на использование EXISTS (хотя для этого потребуется проверка в предложении HAVING, что может быть не очень хорошо)
SELECT IF (m.from_uid = 1, m.to_uid, m.from_uid) AS uid,
m.message,
m.sended AS `date`,
IF (m.from_uid = 1, CONCAT(to_conv.first_name, ' ', to_conv.last_name), CONCAT(from_conv.first_name, ' ', from_conv.last_name)) AS conversation_name
FROM users u
LEFT JOIN messages m
ON m.from_uid = u.user_id
LEFT JOIN users to_conv
ON to_conv.user_id = m.to_uid
LEFT JOIN users from_conv
ON from_conv.user_id = m.from_uid
WHERE EXISTS
(
SELECT MAX(message_id) AS max_message_id
FROM messages
WHERE to_uid = 1
OR from_uid = 1
GROUP BY LEAST(from_uid, to_uid), GREATEST(from_uid, to_uid)
HAVING m.message_id = max_message_id
)
ORDER BY date DESC
LIMIT 5