Индексы работают с предложением "IN"

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

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

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

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

Ответ 5

@Mike: Спасибо за подробный анализ. Есть определенные интересные моменты, которые вы там делаете. Пример, который я опубликовал, несколько тривиален, но основа вопроса возникла из использования 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)

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

Ответ 6

Select EmployeeId From Employee USE(INDEX(EmployeeTypeId))

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