Работают ли индексы с предложением “IN”

StackOverflow https://stackoverflow.com/questions/31500

  •  09-06-2019
  •  | 
  •  

Вопрос

Если у меня есть запрос типа:

Select EmployeeId 
From Employee 
Where EmployeeTypeId IN (1,2,3)

и у меня есть индекс на EmployeeTypeId поле, SQL server все еще использует этот индекс?

Это было полезно?

Решение

Да, именно так.Если ваша таблица employee содержит 10 000 записей, и только 5 записей имеют employeetypeID в (1,2,3), то, скорее всего, для извлечения записей будет использоваться индекс.Однако, если он обнаружит, что 9000 записей имеют employeeIDType в (1,2,3), то, скорее всего, он просто выполнит сканирование таблицы, чтобы получить соответствующие EmployeeID, поскольку быстрее просто просмотреть всю таблицу, чем переходить к каждой ветви дерева индексов и просматривать записи по отдельности.

SQL Server делает много всего, чтобы попытаться оптимизировать выполнение запросов.Однако иногда на этот вопрос не дается правильного ответа.Если вы знаете, что SQL Server не использует индекс, просмотрев план выполнения в query analyzer, вы можете указать механизму запросов использовать определенный индекс со следующим изменением вашего запроса.

Select EmployeeId From Employee WITH (Index(Index_EmployeeTypeId )) Where EmployeeTypeId IN (1,2,3)

Предполагая, что индекс, который у вас есть в поле EmployeeTypeId, называется Index_EmployeeTypeId .

Другие советы

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

Если в последнее время технология не улучшилась настолько, насколько я не могу себе представить, показанный запрос "IN" выдаст результат, который фактически представляет собой набор из трех результирующих наборов, по одному для каждого из значений в списке "IN".Предложение IN становится условием равенства для каждого элемента списка и при необходимости будет использовать индекс.В случае уникальных идентификаторов и достаточно большой таблицы я бы ожидал, что оптимизатор будет использовать индекс.

Однако, если элементы в списке должны были быть неуникальными, и я предполагаю в примере, что "TypeId" является внешним ключом, тогда меня больше интересует распределение.Мне интересно, будет ли оптимизатор проверять статистику для каждого значения в списке?Допустим, он проверяет первое значение и обнаруживает, что оно находится в 20% строк (достаточно большой таблицы, чтобы иметь значение).Вероятно, это будет сканирование таблицы.Но будет ли тот же план запроса использоваться для двух других, даже если они уникальны?

Вероятно, это спорно - что-то вроде таблицы Employee, вероятно, будет достаточно маленьким, чтобы оно оставалось кэшированным в памяти, и вы, вероятно, в любом случае не заметили бы разницы между этим и индексированным извлечением.

И, наконец, пока я проповедую, остерегайтесь запроса в предложении IN:часто это быстрый способ заставить что-то работать и (по крайней мере, для меня) может быть хорошим способом выразить требование, но почти всегда его лучше переформулировать как объединение.Ваш оптимизатор может быть достаточно умен, чтобы заметить это, но опять же, может и нет.Если вы в данный момент не проводите сверку производительности с объемами производственных данных, сделайте это - в наши дни оптимизации, основанной на затратах, вы не можете быть уверены в плане запроса, пока не получите полную загрузку и репрезентативную статистику.Если вы не можете, то будьте готовы к неожиданностям на производстве...

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

Используется ли индекс, зависит не столько от типа запроса, сколько от типа и распределения данных в таблице (таблицах), от того, насколько актуальна статистика вашей таблицы и фактический тип данных столбца.

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

  • Запрос получит доступ не более чем к определенному проценту проиндексированных строк (скажем, ~ 10%, но должен варьироваться в зависимости от СУБД).
  • В качестве альтернативы, если в столбце много строк, но относительно мало уникальных значений, сканирование таблицы также может оказаться более быстрым.

Другая переменная, которая может быть не столь очевидной, - это проверка того, что типы данных сравниваемых значений совпадают.В PostgreSQL я не думаю, что индексы будут использоваться, если вы фильтруете с плавающей точкой, но ваш столбец состоит из целых чисел.Есть также некоторые операторы, которые не поддерживают использование индекса (опять же, в PostgreSQL оператор ILIKE выглядит следующим образом).

Однако, как уже отмечалось, всегда проверяйте анализатор запросов, если сомневаетесь, и документация вашей СУБД - ваш друг.

@Майк:Спасибо за подробный анализ.Определенно, есть несколько интересных замечаний, которые вы там высказываете.Пример, который я опубликовал, несколько тривиален, но в основу вопроса легло использование NHibernate.

С помощью NHibernate вы можете написать предложение, подобное этому:

int[] employeeIds = new int[]{1, 5, 23463, 32523};
NHibernateSession.CreateCriteria(typeof(Employee))
.Add(Restrictions.InG("EmployeeId",employeeIds))

Затем NHibernate генерирует запрос, который выглядит следующим образом

select * from employee where employeeid in (1, 5, 23463, 32523)

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

Select EmployeeId From Employee USE(INDEX(EmployeeTypeId))

Этот запрос будет выполнять поиск по созданному вами индексу.У меня это работает.Пожалуйста, попробуйте..

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top