У меня есть таблица 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 последующем поле в индексе?
Какая версия 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 столбца. В этой ситуации подход UNION
может быть или не быть лучше. Существует множество накладных расходов на сбор данных в таблице темпов. Временная таблица была недавно устранена, но только для определенных случаев UNION ALL
.
Да, используйте UNION ALL
. Это устраняет сортировку и, возможно, дополнительную таблицу temp.
Тест с большим набором данных. Для строк менее 1K производительность вряд ли будет иметь значение.
Правило Thumb в упорядочении столбцов в индексе:
=
тест (ы)IN
, если таковые имеютсяBETWEEN
, <
и т.д.), если таковой имеетсяМоя поваренная книга
Существуют и другие оптимизации, которые зависят от того, что находится в *
в SELECT *
.
Для вашего второго запроса используйте union all
а не union
. Вы всегда хотите union all
, если вы не хотите брать накладные расходы для удаления дубликатов.
Я бы предположил, что вы выиграете от второго запроса по более крупным данным. Я не думаю, что MySQL поддерживает skip-сканирование индексов, поэтому индекс используется только для group_id
и type_id
, но не напрямую для даты.