MySQL не будет использовать доступные индексы в JOIN, если в предложении ON используется OR - PullRequest
3 голосов
/ 14 февраля 2012

Допустим, у вас есть 5 таблиц, каждый из которых имеет столбцы:

  • дом (id, name, street_id)
  • street (id, name)
  • photo(идентификатор, имя)
  • house_photo (house_id, photo_id)
  • street_photo (street_id, photo_id)

И произнесите все столбцы «id» и столбец, заканчивающийся на «У _id 'уже есть индексы.

(На самом деле мой запрос не имеет ничего общего с домами или улицами. Они ради аргументов.)

Теперь скажем, что вы хотели бы получить каждую улицу вколонке, и если на этой улице или в ее домах есть фотографии, вам нужны фотографии в следующей колонке ...

Хитрость здесь заключается в том, что все дома находятся в одной таблице.И все фото в другой таблице.Но чтобы связать 2, нам нужно получить доступ ко всем пяти таблицам.

Я пришел к следующему запросу, содержащему 4 JOIN:

SELECT
    street.name
    ,group_concat(distinct photos.name SEPARATOR '\n') as photos
FROM
    house
    INNER JOIN street ON
        house.street_id = street.id
    LEFT JOIN house_photos ON
        house.id = house_photos.house_id
    LEFT JOIN street_photos ON
        street.id = street_photos.street_id
    LEFT JOIN photos ON
        photos.id = house_photos.photo_id
        OR photos.id = street_photos.photo_id
GROUP BY
    street.name

Различное, чтобы отфильтровать двойные числа,поскольку они будут сгенерированы, когда у вас есть более 1 фотография для дома и более 1 фотография для улицы этого дома.(Carthesian product) Но это не имеет никакого отношения к моей проблеме.

Проблема, с которой я столкнулся, заключается в том, что запрос СУПЕР медленный.(Более 1 минуты или даже больше, чтобы закончить)

Когда я прошу MySQL проанализировать запрос («объяснить расширенный»), я вижу, что он не будет использовать доступные индексы при обработке этого последнего JOIN (которыйв операторе ON указано ИЛИ).

Если я разделю последнее СОЕДИНЕНИЕ на 2 СОЕДИНЕНИЯ (добавив, таким образом, пятое СОЕДИНЕНИЕ), запрос снова станет очень быстрым (для завершения потребуется доля секунды).

SELECT
    street.name
    ,concat(
        group_concat(distinct photos_from_house.name SEPARATOR '\n')
        ,'\n'
        ,group_concat(distinct photos_from_street.name SEPARATOR '\n')
    ) as photos
FROM
    house
    INNER JOIN street ON
        house.street_id = street.id
    LEFT JOIN house_photos ON
        house.id = house_photos.house_id
    LEFT JOIN street_photos ON
        street.id = street_photos.street_id
    LEFT JOIN photos photos_from_house ON
        photos_from_house.id = house_photos.photo_id
    LEFT JOIN photos photos_from_street ON
        photos_from_street.id = street_photos.photo_id
GROUP BY
    street.name

Мой вопрос теперь таков: почему вводится OR в предложении ON, а MySQL не использует доступные индексы / ключи для этого JOIN?

Я уже УЖЕ попытался использовать USE INDEX иИНДЕКС СИЛЫ, но он не сдвинется с места.

Любые объяснения / помощь приветствуются.

1 Ответ

4 голосов
/ 14 февраля 2012

Документы MySQL говорят следующее:

Сведите к минимуму ключевые слова OR в предложениях WHERE. Если нет индекса что помогает найти значения по обе стороны от ИЛИ, любая строка может потенциально может быть частью результирующего набора, поэтому все строки должны быть проверены, и это требует полного сканирования таблицы. Если у вас есть один индекс, который помогает оптимизировать одну сторону запроса ИЛИ и другой индекс, который помогает оптимизировать другую сторону, используйте оператор UNION для быстрого запуска отдельных запросы и объединить результаты позже.

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

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