Долгое время ловит, первый раз задающий вопрос. ;-)
Использование 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
Кроме того, если я изменю его с диапазона на определенную дату, индекс также работает.
(Мне нужно угадать некоторые вещи, так как вы не предоставили 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. Этот процесс более дорогостоящий, чем просто пребывание в индексе, поэтому оптимизатор не хочет этого делать, если расчетное количество строк не является "маленьким".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 и т.д.
Просто угадывая здесь без лишних данных, но добавление новой таблицы в JOIN изменяет распределение данных.
Поэтому, если в первом случае условие WHERE возвращает, вероятно, небольшой (относительно) процент данных, во втором случае оптимизатор решает, что вы получите более быстрые результаты без использования индекса, поскольку такие же условия могут быть не столь избирательными для новая партия данных.
Для лучшего ответа добавьте определения таблиц и COUNT для обоих запросов, как итоговые, так и основанные на ваших запросах.
если вы используете 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 правильный индекс или нет