Почему SQL Server игнорирует индекс?

16

У меня есть таблица CustPassMasterс 16 столбцами, один из которых CustNum varchar(8), и я создал индекс IX_dbo_CustPassMaster_CustNum. Когда я запускаю свое SELECTзаявление:

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

Он полностью игнорирует индекс. Это смущает меня, так как у меня есть еще одна таблица CustDataMasterс большим количеством столбцов (55), один из которых - CustNum varchar(8). Я создал индекс для этой колонки ( IX_dbo_CustDataMaster_CustNum) в этой таблице и использую практически тот же запрос:

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

И он использует индекс, который я создал.

Есть ли какие-то конкретные причины этого? Зачем использовать индекс от CustDataMaster, а не от CustPassMaster? Это из-за низкого количества столбцов?

Первый запрос возвращает 66 строк. Для второго возвращается 1 строка.

Кроме того, дополнительное примечание: CustPassMasterимеет 4991 записей и CustDataMasterимеет 5376 записей. Может ли это быть причиной игнорирования индекса? CustPassMasterтакже имеет дубликаты записей, которые имеют одинаковые CustNumзначения. Это еще один фактор?

Я основываю эту претензию на фактических результатах плана выполнения обоих запросов.

Вот DDL для CustPassMaster(с неиспользованным индексом):

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

И DDL для CustDataMaster(я опустил много несущественных полей):

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

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

Не обращайте внимания на тот факт, что типы данных не полностью соответствуют типу хранимых данных. Эти поля являются резервной копией из базы данных IBM AS / 400 DB2, и это совместимые типы данных для нее. (Я должен иметь возможность запрашивать эту резервную копию базы данных с точно такими же запросами и получать точно такие же результаты.)

Эти данные используются только для SELECTзаявлений. Я не делаю никаких INSERT/ UPDATE/ DELETEутверждений на нем, за исключением случаев, когда приложение резервного копирования копирует данные из AS / 400.

Der Kommissar
источник
Возможно, стоит прочитать эту статью о переломном моменте от NonClustered к Clustered. sqlskills.com/blogs/kimberly/the-tipping-point-query-answers
Марк Синкинсон
3
Так вот в чем разница. Если первый запрос использует ваш индекс, он должен выполнить 65 поисков. Это дорого Второй запрос должен выполнить только один.
Аарон Бертран

Ответы:

18

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

Казалось бы, оптимизатор, основанный на затратах, считает, что использование рассматриваемого индекса будет более дорогим. Вы можете увидеть, как он использует индекс, если вместо этого SELECT *вы просто SELECT T1Col1.

Когда вы SELECT *говорите SQL Server вернуть все столбцы в таблице. Чтобы вернуть эти столбцы, SQL Server должен прочитать страницы строк, соответствующих WHEREкритериям оператора, из самой таблицы (кластеризованный индекс или куча). SQL Server, вероятно, считает, что количество операций чтения, необходимых для получения остальных столбцов из таблицы, означает, что он может также сканировать таблицу напрямую. Было бы полезно увидеть фактический запрос и фактический план выполнения, используемый запросом.

Макс Вернон
источник
3
Таким образом, более очевидное и оптимальное решение было бы для меня ограничить столбцы, которые я выбрал, и включить их в INCLUDEпредложение индекса?
Der Kommissar
1
Это вполне может иметь большое значение. Добавление всех столбцов, возвращаемых запросом к INCLUDEпредложению, вероятно, заставит SQL Server использовать индекс. Сказав это, что вы пытаетесь оптимизировать? Мне кажется, если ваша таблица имеет средний размер строки в 100 байт, то 5000 строк - это всего лишь около 500 КБ данных, и на них, возможно, не стоит тратить время.
Макс Вернон
1
Средний размер строки составляет 0,30 КБ для Table1, и 0,53 КБ для Table2. Все эти данные импортируются из AS / 400 (IBM System i), и ни на одном ПК нет никаких PK. Сегодня я вручную создал все индексы после того, как люди упоминали, что приложение иногда работает довольно медленно.
Der Kommissar
10

Для использования индекса, поскольку вы это делаете select *, SQL Server должен сначала прочитать каждую из строк индекса, которые соответствуют значению, которое вы указали в предложении where. Исходя из этого, он будет получать значения кластеризованного индекса для каждой строки, а затем он должен искать каждое из них отдельно от кластеризованного индекса (= поиск ключа). Поскольку вы сказали, что значения не являются уникальными, SQL Server использует статистику, чтобы оценить, сколько раз ему приходится выполнять поиск ключа.

Скорее всего, оценка затрат на сканирование поисков некластеризованного индекса + ключа превышает оценку затрат на сканирование кластерного индекса, и поэтому индекс игнорируется.

Вы можете попытаться использовать, set statistics io onа затем использовать подсказку индекса, чтобы увидеть, действительно ли меньше стоимость ввода-вывода при использовании индекса или нет. Если разница велика, вы можете посмотреть статистику, если она устарела.

Кроме того, если ваш SQL на самом деле использует переменные, а не точные значения, это также может быть вызвано анализом параметров (= предыдущее значение, использованное для создания плана, содержало много строк в таблице).

Джеймс З
источник
1

Это может быть причиной. Оптимизаторы основаны на стоимости и решают, какой путь выбрать, основываясь на «стоимости», которую имеет каждый путь выполнения. Самая большая стоимость - это передача данных с диска в память. Если оптимизатор подсчитает, что для чтения индекса и данных требуется больше времени, он может решить пропустить индекс. Чем больше строк, тем больше дисковых блоков они занимают.

Marco
источник