2010-12-09 3 views
1

Я использовал запрос, который выглядел похож на этот:Странные Проблемы производительности с INNER JOIN vs. LEFT JOIN

SELECT `episodes`.*, IFNULL(SUM(`views_sum`.`clicks`), 0) as `clicks` 
FROM `episodes`, `views_sum` 
WHERE `views_sum`.`index` = "episode" AND `views_sum`.`key` = `episodes`.`id` 
GROUP BY `episodes`.`id` 

... который принимает ~ 0,1с для выполнения. Но это проблематично, потому что у некоторых episodes нет соответствующей строки views_sum, поэтому эти эпизоды не включены в результат.

То, что я хочу NULL значения, когда соответствующая views_sum строка не существует, поэтому я попытался с помощью LEFT JOIN вместо:

SELECT `episodes`.*, IFNULL(SUM(`views_sum`.`clicks`), 0) as `clicks` 
FROM `episodes` 
LEFT JOIN `views_sum` ON (`views_sum`.`index` = "episode" AND `views_sum`.`key` = `episodes`.`id`) 
GROUP BY `episodes`.`id` 

Этот запрос производит те же столбцы, и он также включает в себя несколько строк отсутствует в первом запросе.

BUT, второй запрос длится в 10 раз! Полная секунда.

Почему существует такое огромное несоответствие между временем выполнения, когда результат настолько схож? Там нигде нет около В 10 раз больше строк - это похоже на 60 из 1-го запроса и 70 из 2-го. Это не говоря уже о том, что 10 дополнительных строк не содержат views!

Любые световые сараи были бы высоко оценены!

(Есть указатели на episodes.id, views_sum.index и views_sum.key.)

EDIT:

скопировать-вставить в SQL сверху, и здесь являются Объясняет, в следующем порядке:

id select_type table  type possible_keys key   key_len  ref       rows Extra 
1 SIMPLE  views_sum ref  index,key  index  27   const      6532 Using where; Using temporary; Using filesort 
1 SIMPLE  episodes eq_ref PRIMARY   PRIMARY  4   db102914_itw.views_sum.key 1  Using where 

id select_type table  type possible_keys key   key_len  ref       rows Extra 
1 SIMPLE  episodes ALL  NULL   NULL  NULL  NULL      70  Using temporary; Using filesort 
1 SIMPLE  views_sum ref  index,key  index  27   const      6532 
+0

Я исправил ваше название, потому что в любом примере нет перекрестного соединения. Перекрестное соединение создает декартово произведение, но первый запрос использует синтаксис внутреннего соединения ANSI-89. – 2010-12-09 06:15:03

+0

Спасибо, OMG. Итак, CROSS JOIN - ВХОДНАЯ ВСТУПЛЕНИЕ без какого-либо сравнения между двумя таблицами? – JKS 2010-12-09 06:20:49

ответ

1

Вот вопрос, который я в конечном итоге придумал после многих итераций. (Флаг SQL_NO_CACHE есть, так что я могу проверить время выполнения.)

SELECT SQL_NO_CACHE e.*, IFNULL(SUM(vs.`clicks`), 0) as `clicks` 

FROM `episodes` e 

LEFT JOIN 
(SELECT * FROM `views_sum` WHERE `index` = "episode") vs 
ON vs.`key` = e.`id` 

GROUP BY e.`id` 

Поскольку ON Condtion views_sum.index = "episode" статичен, т.е. не зависят от ряда он присоединился, я был в состоянии получить массовую производительность boost, сначала используя подзапрос, чтобы ограничить таблицу views_sumдо присоединение.

Мой запрос теперь занимает ~ 0.2 с.И что еще лучше, время не увеличивается по мере увеличения смещения запроса (в отличие от моей первой попытки LEFT JOIN). Он остается прежним, даже если вы делаете сортировку по столбцу clicks.

0

Это все об индексах. Вам придется немного поиграть с ним или опубликовать схему базы данных. Так же, как грубое предположение, я бы сказал, что вы должны убедиться, что у вас есть указатель на views_sum.key.

+0

В самом конце моего вопроса. Спасибо за эту мысль. – JKS 2010-12-09 06:05:47

0

Обычно LEFT JOIN будет более медленным, чем INNER JOIN, или CROSS JOIN, поскольку он должен просматривать первый стол по-разному. Иными словами, разница во времени не связана с размером результата, но полный размер левой таблицы.

Я также задаюсь вопросом, хотите ли вы попросить MySQL разобраться в том, что вы должны делать сами. В частности, что функция SUM() обычно требует условия GROUP BY.

+0

Да, я забыл включить `GROUP BY episode.id`, извините! – JKS 2010-12-09 06:07:26

+0

Одна вещь, которую я бы постарался, - это перевести выражение `views_sum.index =" episode "` в `WHERE`, но вы также получите дополнительную информацию, помещая весь запрос через` EXPLAIN`. – staticsan 2010-12-09 06:15:24

1

У вас должен быть объединенный индекс на views_sum. index и views_sum. key. Я подозреваю, что вы всегда будете использовать оба поля вместе, если я посмотрю имена. Кроме того, я бы переписал первый запрос, чтобы использовать правильное предложение INNER JOIN вместо фильтрованного декартова продукта.

Я подозреваю, что выполнение обоих запросов будет намного ближе, если вы это сделаете. И, что более важно, намного быстрее, чем сейчас.

Редактирование: Думая об этом, я бы добавил к этому индексу третий столбец: views_sum. clicks, который, вероятно, может использоваться для SUM. Но помните, что индексы с несколькими столбцами можно использовать только слева направо.

Смежные вопросы