SQL-запрос не использует доступный индекс (SQL Server 2008)

У меня есть следующая таблица в SQL Server 2008:

Session
(
sessionid varchar(10)
startdate dateteime
enddate dateteime
--rest of the fields go here
)

У меня есть следующие два некластеризованных индекса:

Inddex1: SessionID,startdate,enddate
Inddex2: startdate,enddate

У меня есть следующий запрос

select *
from session
where startdate>=@date1 and enddate <=@date2

при выполнении этого запроса обе эти idexes не используются. План запроса показывает только сканирование таблицы.

Теперь я попытался удалить index1 и выполнить тот же SP. Тем не менее, index2 не используется.

Любые подсказки о том, как заставить SP использовать index2? (Пожалуйста, не используйте принудительное использование индекса.)


sql sql-server-2008 indexing
person Community    schedule 14.05.2009    source источник


Ответы (5)


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

Сколько строк в таблице и какова кардинальность/уникальность значений? Если таблица достаточно мала, сканирование таблицы может быть более эффективным, чем поиск по индексу + поиск по закладкам для извлечения оставшихся столбцов.

person Mark S. Rasmussen    schedule 14.05.2009
comment
+1 - маленькие таблицы меня долго выкидывали из-за того, что во многих случаях они отказывались использовать индексы (наверное, зря) - person Michael Haren; 14.05.2009

Во-первых, неплохо иметь первичный ключ, который вы используете в качестве уникального кластеризованного индекса.

Я не уверен, что понимаю смысл этих двух составных индексов. Не лучше ли с индивидуальным индексом по датам?

person Cybergibbons    schedule 14.05.2009

Вы говорите, что обработчик запросов не использует один из ваших индексов, но не говорите нам, что он делает. Я предполагаю сканирование стола...?

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

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

Рассмотрите возможность изменения запроса и/или индекса, чтобы выбирать только те поля, которые полностью покрываются индексом. Тогда я ожидаю, что вы увидите использование индекса. Но это не всегда правильно — создание огромного индекса для редко используемого запроса может быть неправильным.

person Martin    schedule 14.05.2009

Любой индекс с начальной датой в качестве первого столбца может помочь в поиске startdate>=@date1. Любой индекс с "датой окончания" в качестве первого столбца может помочь в поиске "дата окончания ‹=@date2". Но нет индекса, который может помочь с обоими.

И если предположить, что даты окончания с одинаковой датой начала практически отсутствуют, индекс (дата начала, дата окончания) не более полезен, чем индекс (дата начала).

Таким образом, сканирование таблицы кажется хорошим выбором для Sql Server.

Даже если вы ищете startdate>=@date1, все равно имеет смысл выполнить сканирование таблицы. Сканирование индекса даст вам много ссылок на таблицу, которую вам придется разрешить, что не стоит того для большого количества данных.

Один запрос, который должен использовать индекс:

select * from session where startdate = @date1

В любом случае, если вы думаете, что знаете лучше, чем оптимизатор, вы можете принудительно использовать индекс, например:

select *
from session with (index indexname)
where startdate>=@date1 and enddate <=@date2
person Andomar    schedule 14.05.2009
comment
Не правда. И такой индекс, как (startdate, enddate), отлично подходит для запроса с предикатом, который сначала фильтрует по дате начала, а затем по дате окончания. Индекс можно использовать. Если столбцов много, фильтрация только по этому индексу и поиск результатов по закладкам будет намного дешевле с точки зрения ввода-вывода, чем полное сканирование таблицы. - person Mark S. Rasmussen; 16.05.2009
comment
Это поможет со звездной датой = @a и конечной датой ‹ @b и предотвратит поиск в таблице в select enddate, где startdate › @x. Так что да, бывают редкие исключения. - person Andomar; 16.05.2009
comment
Это также поможет в его точном запросе. Наличие такого индекса, как (startdate,enddate) поможет в запросе с таким предикатом, как WHERE startdate ›= @startdate AND enddate ‹= @enddate Поскольку индекс в основном представляет собой просто отсортированные данные, он намного быстрее способ фильтрации данных. Используя первый предикат и индекс, мы находим все данные, удовлетворяющие первому предикату. Учитывая индекс, найденные данные теперь сортируются в соответствии со вторым предикатом, и, таким образом, мы можем снова отфильтровать их. - person Mark S. Rasmussen; 16.05.2009
comment
Интересно. Начните с бинарного поиска по индексу (startdate ›= @startdate). Результат не отсортирован по дате окончания, так что это сканирование индекса по половине индекса. Затем поиск строк результатов. Этот поиск вреден для большого количества строк, поэтому я думаю, что Sql Server предпочтет сканирование таблицы методом грубой силы. - person Andomar; 16.05.2009

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

person hopethisworks    schedule 14.05.2009
comment
Не правда. Индекс может (акцент на может) использоваться всякий раз, когда есть предикат в первом столбце индекса. Не обязательно равенство. - person Mark S. Rasmussen; 14.05.2009