Оптимизация сложного запроса
Я пытаюсь оптимизировать самый длинный запрос, который я когда-либо писал, используя MySQL EXPLAIN
но так как это мой первый, я не могу понять результат. Вот запрос и результат запуска EXPLAIN
команда:
EXPLAIN SELECT pb.name, s1.MessageFrom, s1.MessageText, s1.SendTime, s1.is_unread, s1.Id, s1.autoreply_sent FROM sol_inbound s1
JOIN sol_contactnum c ON s1.MessageFrom = c.number
JOIN sol_phonebk_contactnum USING (contactnum_id)
JOIN sol_phonebk pb USING (phonebk_id)
JOIN sol_message_folder mf ON s1.Id = mf.message_id
WHERE (MessageFrom, SendTime) IN (SELECT MessageFrom, MAX(SendTime) FROM sol_inbound inb
JOIN sol_message_folder mf WHERE inb.Id = mf.message_id
AND mf.folder_id=1 AND mf.direction='inbound' AND mf.user_id=1
GROUP BY MessageFrom)
AND mf.folder_id=1 AND mf.direction='inbound' AND mf.user_id=1
UNION
SELECT NULL `name`, s1.MessageFrom, s1.MessageText, s1.SendTime, s1.is_unread, s1.Id, s1.autoreply_sent FROM sol_inbound s1
LEFT JOIN sol_contactnum c ON s1.MessageFrom = c.number
JOIN sol_message_folder mf ON s1.Id = mf.message_id
WHERE c.number IS NULL
AND mf.folder_id=1 AND mf.direction='inbound' AND mf.user_id=1
AND (MessageFrom, SendTime) IN (SELECT MessageFrom, MAX(SendTime) FROM sol_inbound inb
JOIN sol_message_folder mf WHERE inb.Id = mf.message_id
AND mf.folder_id=1 AND mf.direction='inbound' AND mf.user_id=1
GROUP BY MessageFrom)
ORDER BY SendTime DESC LIMIT 100
EXPLAIN
результаты в:
id select_type table type possible_keys key key_len ref rows Extra
------ ------------------ ---------------------- ------ ------------------------------------------------------------- ---------------- ------- ---------------------------------------------------- ------ ------------------------
1 PRIMARY pb ALL PRIMARY (NULL) (NULL) (NULL) 303
1 PRIMARY sol_phonebk_contactnum ref PRIMARY,phonebk_id1_idx,contactnum_id1_idx,phonebk_contactnum PRIMARY 4 googlep1_solane.pb.phonebk_id 1 Using index
1 PRIMARY c eq_ref PRIMARY,number_idx PRIMARY 4 googlep1_solane.sol_phonebk_contactnum.contactnum_id 1
1 PRIMARY s1 ref PRIMARY,message_from_idx message_from_idx 243 googlep1_solane.c.number 1 Using where
1 PRIMARY mf eq_ref PRIMARY PRIMARY 22 const,googlep1_solane.s1.Id,const,const 1 Using where; Using index
2 DEPENDENT SUBQUERY inb index PRIMARY message_from_idx 243 (NULL) 1
2 DEPENDENT SUBQUERY mf eq_ref PRIMARY PRIMARY 22 const,googlep1_solane.inb.Id,const,const 1 Using where; Using index
3 UNION s1 ALL PRIMARY (NULL) (NULL) (NULL) 877 Using where
3 UNION c ref number_idx number_idx 243 googlep1_solane.s1.MessageFrom 1 Using where; Using index
3 UNION mf eq_ref PRIMARY PRIMARY 22 const,googlep1_solane.s1.Id,const,const 1 Using where; Using index
4 DEPENDENT SUBQUERY inb index PRIMARY message_from_idx 243 (NULL) 1
4 DEPENDENT SUBQUERY mf eq_ref PRIMARY PRIMARY 22 const,googlep1_solane.inb.Id,const,const 1 Using where; Using index
(NULL) UNION RESULT <union1,3> ALL (NULL) (NULL) (NULL) (NULL) (NULL) Using filesort
UNION
в середине запроса соединяются те, чьи номера появляются в телефонной книге, с теми, кто этого не делает (таким образом, LEFT JOIN
).
Редактировать:
Этот запрос получает самое последнее входящее сообщение для каждого номера и возвращает его. я могу использовать GROUP BY
так как он возвращает самое старое сообщение... мне нужно самое последнее. Затем он соединяет эти номера, которых нет в телефонной книге, поэтому я проверяю WHERE c.number IS NULL.
2 ответа
Кажется, вы использовали 2 sub-query's
чтобы отфильтровать результаты, они на самом деле не нужны. Я полагаю, вам нужно отобразить latest message
с каждого MessageFrom
Я бы.
Попробуйте это для более быстрых результатов
SELECT
*
FROM
( SELECT
pb.name,
s1.MessageFrom,
s1.MessageText,
s1.SendTime,
s1.is_unread,
s1.Id,
s1.autoreply_sent,
@row_num := IF(@prev_value=s1.MessageFrom,@row_num+1,1) AS row_num,
@prev_value := s1.MessageFrom
FROM
sol_inbound s1
JOIN sol_contactnum c ON s1.MessageFrom = c.number
JOIN sol_phonebk_contactnum USING (contactnum_id)
JOIN sol_phonebk pb USING (phonebk_id)
JOIN sol_message_folder mf ON s1.Id = mf.message_id
WHERE
mf.folder_id=1
AND mf.direction='inbound'
AND mf.user_id=1
ORDER BY
s1.MessageFrom,
s1.sendTime desc ) temp WHERE temp.row_num = 1
UNION
SELECT
*
FROM
(
SELECT
NULL `name`,
s1.MessageFrom,
s1.MessageText,
s1.SendTime,
s1.is_unread,
s1.Id,
s1.autoreply_sent,
@row_num := IF(@prev_value=s1.MessageFrom,@row_num+1,1) AS row_num,
@prev_value := s1.MessageFrom
FROM
sol_inbound s1
LEFT JOIN sol_contactnum c ON s1.MessageFrom = c.number
JOIN sol_message_folder mf ON s1.Id = mf.message_id
WHERE
c.number IS NULL
AND mf.folder_id=1
AND mf.direction='inbound'
AND mf.user_id=1
ORDER BY
s1.MessageFrom,
s1.sendTime desc
) temp2 WHERE temp2.row_num = 1
ORDER BY
SendTime DESC
LIMIT 100
Вместо использования sub-query
чтобы отфильтровать результаты, я использовал session vars
в Mysql, чтобы сохранить ранг всех сообщений MessageFrom
, Тот, имеющий latest sendtime
имеет rank
как 1
Где возможно, используйте некоррелированный подзапрос следующим образом...
FROM... x
JOIN
( SELECT MessageFrom
, MAX(SendTime) max_sendtime
FROM sol_inbound inb
JOIN sol_message_folder mf
ON inb.Id = mf.message_id
WHERE mf.folder_id=1
AND mf.direction='inbound'
AND mf.user_id=1
GROUP
BY MessageFrom
) y
ON y.messagefrom = x.messagefrom
AND y.max_sendtime = x.sendtime