Странные проблемы с производительностью INNER JOIN против LEFT JOIN - PullRequest
1 голос
/ 09 декабря 2010

Я использовал запрос, похожий на этот:

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`

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

НО , второй запрос занимает в 10 раз больше времени! Целую секунду.

Почему существует такое огромное расхождение между временем выполнения, когда результат так похож? Нигде около в 10 раз больше строк - это как 60 из первого запроса и 70 из второго. Не говоря уже о том, что 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 

Ответы [ 4 ]

1 голос
/ 10 декабря 2010

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

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

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

1 голос
/ 10 декабря 2010

Вот запрос, который я в конечном итоге сформулировал после многих итераций. (Флаг 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 views_sum.index = "episode" является статическим, т. Е. Не зависит от строки, к которой он присоединен, я смог значительно повысить производительность, сначала воспользовавшись подзапросом для ограничения таблицы views_sum . до присоединения .

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

0 голосов
/ 09 декабря 2010

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

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

0 голосов
/ 09 декабря 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...