Как MySQL использует многопольные индексы для запросов с ИЛИ в среднем поле индекса?

0

У меня есть таблица InnoDB с многоколоновым неидеальным индексом (group_id, type_id, expiry_date).

В случае выполнения запроса SELECT * FROM Items WHERE group_id = 1 AND type_id IN (1,2,3) AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01'

Будет ли индекс работать нормально, поскольку я использую IN для второго поля индекса, и они дополнительно имеют диапазон для 3-го числа или я должен извлечь выгоду из этого?

SELECT * FROM Items WHERE group_id = 1 AND type_id = 1 AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01' UNION SELECT * FROM Items WHERE group_id = 1 AND type_id = 2 AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01' UNION SELECT * FROM Items WHERE group_id = 1 AND type_id = 3 AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01'

EXPLAIN показывает идентичные планы запросов для обоих запросов, но у меня есть довольно небольшая таблица для тестирования и не уверен, что оптимизатор запросов будет действовать одинаково на большом количестве данных.

И как вообще индекс работает для случая с использованием IN/OR/BETWEEN на 2 последующем поле в индексе?

  • 0
    Создайте план выполнения для обоих и убедитесь сами. Я догадываюсь, что IN намного быстрее, чем использование union, в то время как union all должен быть ближе или быстрее, чем IN.
  • 0
    Ваш первый запрос с IN должен иметь возможность использовать этот индекс и вести себя намного быстрее, чем второй, даже в случае больших данных.
Показать ещё 1 комментарий
Теги:
indexing
query-optimization
innodb

2 ответа

0

Какая версия MySQL/MariaDB? Недавно были оптимизация; не сейчас, если они помогут здесь.

У вас есть возможная ошибка - включая дополнительный день в AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01'. Изменить на

AND expiry_date >= '2017-01-01'
AND expiry_date  < '2017-01-01' + INTERVAL 1 YEAR

(Это считается единственным критерием "диапазона". BETWEEN - это также тест диапазона, но он "включен", следовательно, "ошибка".)

Я бы просто имел два составных индекса (если бы я не смог найти реальный ответ на ваш вопрос):

(group_id, type_id, expiry_date)
(group_id, expiry_date)

Случай 1: Оптимизатор может пройти мимо IN: тогда работает первый индекс.

Случай 2: Оптимизатор не может пройти мимо IN: Затем происходит одно из следующих событий:

  • Список IN имеет только один элемент. Затем он преобразуется из IN в =, и первый индекс является оптимальным, при этом используются все 3 столбца.
  • Оптимизатор лучше определяет первый индекс - маленький список IN, большой диапазон дат.
  • Оптимизатор решает, что диапазон дат лучше (меньший диапазон) и выбирает второй индекс.

В этой ситуации подход UNION может быть или не быть лучше. Существует множество накладных расходов на сбор данных в таблице темпов. Временная таблица была недавно устранена, но только для определенных случаев UNION ALL.

Да, используйте UNION ALL. Это устраняет сортировку и, возможно, дополнительную таблицу temp.

Тест с большим набором данных. Для строк менее 1K производительность вряд ли будет иметь значение.

Правило Thumb в упорядочении столбцов в индексе:

  1. = тест (ы)
  2. IN, если таковые имеются
  3. один "диапазон" (BETWEEN, < и т.д.), если таковой имеется
  4. Подумайте о создании "покрывающего" индекса.

Моя поваренная книга

Существуют и другие оптимизации, которые зависят от того, что находится в * в SELECT *.

0

Для вашего второго запроса используйте union all а не union. Вы всегда хотите union all, если вы не хотите брать накладные расходы для удаления дубликатов.

Я бы предположил, что вы выиграете от второго запроса по более крупным данным. Я не думаю, что MySQL поддерживает skip-сканирование индексов, поэтому индекс используется только для group_id и type_id, но не напрямую для даты.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню