Почему SQL Server иногда выбирает сканирование индекса вместо поиска по закладке? - PullRequest
3 голосов
/ 12 декабря 2011

у нас есть простая таблица, такая как:

OrderID primary key / clustered index
CustomerID foreign key / single-column non-clustered index
[a bunch more columns]

Тогда у нас есть такой запрос:

SELECT [a bunch of columns]
FROM Orders
WHERE CustomerID = 1234

Мы обнаруживаем, что иногда SQL Server 2008 R2 выполняет поиск по некластеризованному индексу, а затем выполняет поиск по закладкам кластерного индекса (нам это нравится - это довольно быстро).

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

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

Сначала я хотел бы услышать любые идеи, почему SQL Server делает то, что делает. Кроме того, любые рекомендации будут наиболее цениться. Спасибо!

Ответы [ 2 ]

4 голосов
/ 12 декабря 2011

Селективность CustomerID сыграет определенную роль в решении оптимизатора запросов. Если, с одной стороны, он был уникальным, то операция равенства даст не более одного результата, поэтому операция SEEK / LOOKUP почти гарантирована. Если, с другой стороны, потенциально сотни или тысячи записей будут соответствовать значению CustomerID, сканирование кластерного индекса может показаться более привлекательным.

Вы будете удивлены, насколько избирательным должен быть фильтр, чтобы исключить сканирование. Я не могу найти статью, из которой первоначально извлек эту цифру, но если CustomerID 1234 будет соответствовать всего 4% записей в таблице, сканирование кластерного индекса может быть более эффективным или, по крайней мере, выглядеть так оптимизатор (который не делает это правильно 100% времени).

Звучит, по крайней мере, правдоподобно, что статистика, хранящаяся в некластеризованном индексе CustomerID, заставляет оптимизатор переключаться между поиском и сканированием на основе критериев селективности.

Возможно, вы сможете уговорить оптимизатор к использованию индекса, введя операцию JOIN или EXISTS:

-- Be aware: this approach is untested
select o.*
  from Orders o
       inner join Customers c on o.CustomerID = c.CustomerID
 where c.CustomerID = 1234;

Или:

-- Be aware: this approach is untested
select o.*
  from Orders o
 where exists (select 1
                 from Customers c
                where c.CustomerID = 1234 and
                      o.CustomerID = c.CustomerID);

Также имейте в виду, что при таком подходе EXISTS, если у вас нет индекса для предиката «соединения» (в данном случае, поля CustomerID) в обеих таблицах, тогда вы закончите с вложенным циклом, который мучительно медленный. Использование внутренних объединений кажется гораздо безопаснее, но время от времени подход EXISTS имеет свое место, когда он может использовать индексы.

Это всего лишь предложения; Я не могу сказать, будут ли они эффективными или нет. Просто что-то попробовать, или для постоянного эксперта, чтобы подтвердить или опровергнуть.

4 голосов
/ 12 декабря 2011

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...