Индекс при использовании ИЛИ в запросе - PullRequest
3 голосов
/ 26 мая 2019

Каков наилучший способ создания индекса, когда у меня есть такой запрос?

... WHERE (user_1 = '$user_id' OR user_2 = '$user_id') ...

Я знаю, что в запросе можно использовать только один индекс, поэтому я не могу создать два индекса: один для user_1 и один для user_2.

Также может ли решение для этого типа запроса быть использовано для этого запроса?

WHERE ((user_1 = '$user_id' AND user_2 = '$friend_id') OR (user_1 = '$friend_id' AND user_2 = '$user_id'))

Ответы [ 3 ]

4 голосов
/ 27 мая 2019

MySQL испытывает трудности с условиями OR.Теоретически, есть оптимизация слияния индексов, о которой упоминает @duskwuff, но на практике она не срабатывает, когда вы думаете, что это нужно.Кроме того, он не дает такой же производительности, как отдельный индекс.

Решение, которое большинство людей используют для решения этой проблемы, заключается в разделении запроса:

SELECT ... WHERE user_1 = ?
UNION
SELECT ... WHERE user_2 = ?

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

Ваш второй запрос оптимизируется проще.Это просто сравнение кортежей.Это можно записать так:

WHERE (user_1, user_2) IN (('$user_id', '$friend_id'), ('$friend_id', '$user_id'))

В старых версиях MySQL для сравнения кортежей индекс не использовался, но с 5.7.3 он будет использоваться (см. https://dev.mysql.com/doc/refman/5.7/en/row-constructor-optimization.html).

PS: Не интерполируйте переменные кода приложения непосредственно в выражениях SQL. Вместо этого используйте параметры запроса.

1 голос
/ 27 мая 2019

Я знаю, что в запросе можно использовать только один индекс…

Это неверно.При правильных обстоятельствах MySQL будет обычно использовать несколько индексов в запросе.(Например, запрос, объединяющий несколько таблиц, почти всегда будет использовать хотя бы один индекс для каждой задействованной таблицы.)

В случае вашего первого запроса MySQL будет использовать оптимизацию объединения слияния индекса .Если оба столбца проиндексированы, вывод EXPLAIN даст объяснение в виде строк:

Using union(index_on_user_1,index_on_user_2); Using where

Запрос, показанный во втором примере, покрывается индексом (user_1, user_2).Создайте этот индекс, если вы планируете регулярно выполнять эти запросы.

0 голосов
/ 30 мая 2019

Два случая различны.

В первом случае в обоих столбцах необходимо искать одинаковое значение.Если у вас есть индекс из двух столбцов (u1, u2), то он может использоваться в столбце u1, поскольку его нельзя использовать в столбце u2.Если у вас есть два отдельных индекса для u1 и u2, вероятно, оба они будут использованы.Выбор зависит от статистики, основанной на количестве ожидаемых строк.Если ожидаемых возвращаемых строк мало, будет выбран поиск по индексу, если соответствующий индекс доступен.Если число высокое, сканирование предпочтительнее, либо таблица, либо индекс.

Во втором случае снова необходимо снова проверить оба столбца, но в каждом поиске есть два под-поиска, где второй под-поискбудет по результатам первого из-за условия AND.Здесь это имеет значение больше, и два индекса u1 и u2 помогут, так как любое поле, выбранное для поиска в первую очередь, будет иметь индекс.Выбор использования индекса аналогичен описанному выше.

В любом случае, однако, каждое ИЛИ будет вызывать еще 1 поиск или набор поисков.Таким образом, предлагаемое решение разбиения с использованием объединения не мешает больше, так как в таблице будет производиться поиск x раз, независимо от того, выбран ли 1 с помощью OR (s), или x выбирается с помощью объединения, и независимо от выбора индекса и типа поиска (поиск или сканирование).В результате, так как каждый выбор в объединении получает свою собственную часть плана выполнения, более вероятно, что будут использоваться индексы (один столбец) и, наконец, будут получены все наборы результатов строк из всех частей вокруг OR.Если вы не хотите копировать большой оператор выбора во многие объединения, вы можете получить значения первичного ключа, а затем выбрать их или использовать представление, чтобы убедиться, что большинство операторов находится в одном месте.

Наконец,если вы исключите опцию объединения, есть способ обмануть оптимизатор, чтобы он использовал один индекс.Создайте двойной индекс u1, u2 (или u2, u1 - любой столбец с большей мощностью будет первым) и измените свое утверждение так, чтобы все части ИЛИ использовали все столбцы:

... WHERE (user_1 = '$user_id' OR user_2 = '$user_id') ...

будет преобразовано в:

... WHERE ((user_1 = '$user_id' and user_2=user_2) OR (user_1=user_1 and user_2 = '$user_id')) ...

Таким образом, двойной индекс (u1, u2) будет использоваться всегда.Обратите внимание, что это не сработает, если столбцы обнуляются, и пропуск этого значения с помощью isnull или coalesce может привести к тому, что индекс не будет выбран.Однако он будет работать с отключенным значением ANSI.

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