MySQL, диапазон дат idex игнорируется при добавлении второй таблицы в запрос

0

Долгое время ловит, первый раз задающий вопрос. ;-)

Использование PHP 5.6 и MySQL Ver 14.14. Распространение 5.6.41, для Win64 (x86_64). Да, я немного знаю позади, и мы работаем над обновлением. Но это то, где мы сейчас находимся. ;-)

Обновления для задаваемых вопросов: индекс находится в CreateDate. Я думал, что может возникнуть проблема с тем, что столбец является DateTime, поэтому я создал еще один столбец, который был просто датой, установил индекс и повторил, но это не имело никакого эффекта.

ulc имеет 8965 строк. При поиске индексов 3787 et имеет 9530 строк. В запросе, который не использует индекс, он ищет только одну строку при поиске по первому ключу из первого запроса.

Форматирование даты сравнения не имеет большого значения. Я пробовал всевозможные форматы, включая просто "2018-01-01 {00:00:00}". Никаких изменений.

У меня есть то, что я считаю странным, но я подозреваю, что кто-то здесь станет "духом!". один. У меня есть запрос, который включает диапазон дат для первичной таблицы, а затем идет, чтобы получить другие биты данных из других таблиц на основе набора уникальных идентификаторов из первой таблицы. Не волнуйтесь, я приведу примеры ниже. Когда я выполняю поиск только в основной таблице, индекс диапазона работает так, как ожидалось, и ищет только соответствующие строки. Однако, когда я добавляю в следующую таблицу с предложением ON, он игнорирует индекс и ищет все строки первичной таблицы. Если я останусь без предложения on, он вернется к правильному использованию индекса. Я попытался использовать FORCE INDEX (USE проигнорировано), и хотя это делает его использование индекса, он замедляет запрос вниз. В любом случае, вот запросы:

Работает:

select CreateDate
from ulc
Inner Join et
WHERE ulc.CreateDate >= STR_TO_DATE("01/01/2018", "%m/%d/%Y")
AND ulc.CreateDate <= STR_TO_DATE("08/02/2018", "%m/%d/%Y")

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  ulc     range   index_CreateDate    index_CreateDate    5   NULL    3787    Using where; Using index
1   SIMPLE  et  index   NULL    index_BankProcessorProfile  5   NULL    9530    Using index; Using join buffer (Block Nested Loop)

Не работает:

select CreateDate
from ulc
Inner Join et on et.TranID = ulc.TranID
WHERE ulc.CreateDate >= STR_TO_DATE("01/01/2018", "%m/%d/%Y")
AND ulc.CreateDate <= STR_TO_DATE("08/02/2018", "%m/%d/%Y")

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   SIMPLE  ulc     ALL     TranID,index_CreateDate     NULL    NULL    NULL    8965    Using where
1   SIMPLE  et  eq_ref  PRIMARY     PRIMARY     8   showpro.ulc.TranID  1   Using index

Для второго я просто добавил on et.TranID = ulc.TranID

Кроме того, если я изменю его с диапазона на определенную дату, индекс также работает.

  • 0
    На каком столбце (столбцах) существует индекс? Я не думаю, что вы на самом деле сказали нам это. В любом случае, когда вы выполняете внутреннее соединение между двумя таблицами, MySQL имеет открытый диапазон, чтобы решить, какая таблица должна быть слева / справа от этого объединения, поскольку любая возможность логически одинакова и генерирует один и тот же набор результатов. Итак ... что вы можете видеть, это просто оптимизатор, решивший реализовать объединение по-разному, из соображений производительности. Добавьте больше информации к вашему вопросу, и, возможно, больше можно сказать здесь.
  • 0
    Хорошо, добавил эту информацию. Спасибо за указание на это. Я имею в виду, что для меня это было очевидно: я работал над этим столом пару дней! Во всяком случае, мне было интересно узнать об оптимизаторе, но я не понимаю, как это возможно, поскольку второй таблице нужен TranID из первой. А во второй таблице больше строк. Так...???
Показать ещё 6 комментариев
Теги:
indexing
range

3 ответа

0

(Мне нужно угадать некоторые вещи, так как вы не предоставили SHOW CREATE TABLE. Как "длинный таймер", вы должны были это понять.)

Первое предположение, что TranID не PRIMARY KEY из ulc?

Решение состоит в том, чтобы добавить " INDEX(CreateDate, TranID) " INDEX(CreateDate, TranID) в ulc. (Фактически, вы должны заменить существующий INDEX(CreateDate) (второе предположение, что у вас есть этот индекс сейчас).

Теперь я попытаюсь объяснить, почему первый запрос был доволен INDEX(CreateDate) а второй - нет.

В первом запросе индекс INDEX(CreateDate) является "охватывающим" индексом. То есть этот индекс содержит все столбцы ulc, которые необходимы SELECT. Таким образом, почти гарантировано, что использование индекса будет лучше, чем сканирование таблицы. Это будет "сканирование индекса диапазона" этого индекса.

Второй запрос требует как CreateDate, так и TranID, поэтому ваш индекс не будет "покрывать". Существует два способа выполнить первую часть запроса. Но сначала обратите внимание, что (в InnoDB) вторичный индекс имеет все столбцы PRIMARY KEY (третье предположение: это (id)).

  • Сканирование диапазона индекса. Но для того, чтобы получить TranID, он сначала получает id, затем выполняет поиск в PRIMARY KEY/data, чтобы получить TranID. Этот процесс более дорогостоящий, чем просто пребывание в индексе, поэтому оптимизатор не хочет этого делать, если расчетное количество строк не является "маленьким".
  • Поскольку 3787/8965 не является "маленьким", Оптимизатор решает, что, вероятно, быстрее сканировать ALL 8965 строк, отфильтровывая те, которые не нужны.

Мой предложенный индекс "покрывает", тем самым избегая ограничений между индексом и данными. Таким образом, сканирование диапазона диапазона является эффективным.

Ваше наблюдение, что переход на единую дату использовал индекс. Ну, 1 строка из 8965 "маленькая", поэтому индекс (и отскок) считается более быстрым.

Что касается форматирования даты - правда, это не имеет значения. Это объясняется тем, что парсер замечает, что STR_TO_DATE("01/01/2018", "%m/%d/%Y") является константой, которая может быть оценена один раз и делает это.

Моя поваренная книга должна привести вас непосредственно к составному индексу без поцарапания головы над этим Вопросом.

Первый запрос - это "кросс-соединение", так как у него нет предложения ON, чтобы связать таблицы вместе, и он вернет около 35 миллионов строк (9530 * 3787). Второй запрос будет содержать около 3787 строк, а может быть меньше (если некоторые из соединений не смогут найти совпадение).

" как мало изменений между двумя запросами " - Никогда не думайте об этом! Оптимизатор будет зависеть от кажущихся незначительными различий. SELECT CreateDate и SELECT * - огромная разница. Большая часть того, что я сказал о "первом запросе", будет выброшена. Даже если изменить SELECT ChangeDate, x будет достаточно, чтобы сделать большую морщину. Если типы данных TranID в двух таблицах отличаются друг от друга, индексы становятся бесполезными. Etc и т.д.

0

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

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

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

0

если вы используете DateTime в своем запросе, его предложили использовать "ГГГГ-ММ-ДД ЧЧ: ММ: СС", где класс

если вы используете Date в своем запросе, предложили использовать формат "YYYY-MM-DD" в вашем классе. Вы использовали STR_TO_DATE ("01/01/2018", "% m/% d/% Y") который будет выглядеть как "2018-01-01", кажется, все в порядке

вы пытаетесь найти сложность запроса с помощью EXPLAIN

explain select CreateDate
from ulc
Inner Join et on et.TranID = ulc.TranID
WHERE ulc.CreateDate >= STR_TO_DATE("01/01/2018", "%m/%d/%Y")
AND ulc.CreateDate <= STR_TO_DATE("08/02/2018", "%m/%d/%Y")

вы можете проверить, имеют ли et.TranID и ulc.TranID правильный индекс или нет

Ещё вопросы

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