Есть ли способ заставить порядок выполнения MySQL?
Я знаю, что могу изменить способ выполнения MySQL с помощью ключевого слова FORCE INDEX (abc)
. Но есть ли способ изменить порядок выполнения?
Мой запрос выглядит так:
SELECT c.*
FROM table1 a
INNER JOIN table2 b ON a.id = b.table1_id
INNER JOIN table3 c ON b.itemid = c.itemid
WHERE a.itemtype = 1
AND a.busy = 1
AND b.something = 0
AND b.acolumn = 2
AND c.itemid = 123456
У меня есть ключ для каждого отношения/ограничения, которое я использую. Если я запустил объяснение в этом выражении, я вижу, что mysql начинает сначала запрашивать c.
id select_type table type
1 SIMPLE c ref
2 SIMPLE b ref
3 SIMPLE a eq_ref
Однако я знаю, что запрос в порядке a -> b -> c
будет быстрее (я это доказал)
Есть ли способ сказать mysql использовать определенный порядок?
Обновление: как я знаю, что a -> b -> c
работает быстрее.
Вышеупомянутый запрос занимает 1,9 секунды для завершения и возвращает 7 строк. Если я изменил запрос на
SELECT c.*
FROM table1 a
INNER JOIN table2 b ON a.id = b.table1_id
INNER JOIN table3 c ON b.itemid = c.itemid
WHERE a.itemtype = 1
AND a.busy = 1
AND b.something = 0
AND b.acolumn = 2
HAVING c.itemid = 123456
запрос завершается через 0,01 секунды (без использования 10 000 строк).
Однако это не изящное решение, потому что этот запрос является упрощенным примером. В реальном мире я присоединяюсь от c к другим таблицам. Поскольку HAVING
- это фильтр, который выполняется по всему результату, это означало бы, что я вытащил бы некоторые магниты больше записей из db, чем nescessary.
Edit2: Только некоторая информация:
- переменной в этом запросе является c.itemid. Все остальное - фиксированные значения, которые не изменяются.
- Индексы настроены отлично, а mysql выбирает нужные для меня
- между a и b существует отношение 1: n (используется индекс PRIMARY)
- между b и c существует отношение many to many (используется индекс IDX_ITEMID)
точка в том, что mysql должен начать запрашивать таблицу a и работать с ней до c, а не наоборот. Любое изменение для этого.
Решение: Не совсем то, что я хотел, но это, похоже, работает:
SELECT c.*
FROM table1 a
INNER JOIN table2 b ON a.id = b.table1_id
INNER JOIN table3 c ON b.itemid = c.itemid
WHERE a.itemtype = 1
AND a.busy = 1
AND b.something = 0
AND b.acolumn = 2
AND c.itemid = 123456
AND f.id IN (
SELECT DISTINCT table2.id FROM table1
INNER JOIN table2 ON table1.id = table2.table1_id
WHERE table1.itemtype = 1 AND table1.busy = 1)
Ответы
Ответ 1
Возможно, вам нужно использовать STRAIGHT_JOIN
.
http://dev.mysql.com/doc/refman/5.0/en/join.html
STRAIGHT_JOIN
похож на JOIN
, за исключением того, что левая таблица всегда считывается перед правой таблицей. Это может быть использовано для тех (нескольких) случаев, для которых оптимизатор соединения помещает таблицы в неправильный порядок.
Ответ 2
Вы можете попробовать переписать два пути.
- привести некоторые из условий WHERE в JOIN
- вводить подзапросы, даже если они не нужны
Обе вещи могут повлиять на планировщика.
Первое, что нужно проверить, было бы, если обновить статистику.
Ответ 3
Вы можете использовать FORCE INDEX, чтобы принудительно выполнить порядок выполнения, и я сделал это раньше.
Если вы думаете об этом, обычно есть только один заказ, в котором вы могли бы запрашивать таблицы для любого индекса, который вы выбираете.
В этом случае, если вы хотите, чтобы MySQL сначала начал запрашивать a
, убедитесь, что индекс, который вы нажимаете на b
, является тем, который содержит b.table1_id
. MySQL сможет использовать этот индекс только в том случае, если он уже запросил a
.