Учитывая следующий пример:
IF OBJECT_ID('dbo.my_table') IS NOT NULL
DROP TABLE [dbo].[my_table];
GO
CREATE TABLE [dbo].[my_table]
(
[id] int IDENTITY (1,1) NOT NULL PRIMARY KEY,
[foo] int NULL,
[bar] int NULL,
[nki] int NOT NULL
);
GO
/* Insert some random data */
INSERT INTO [dbo].[my_table] (foo, bar, nki)
SELECT TOP (100000)
ABS(CHECKSUM(NewId())) % 14,
ABS(CHECKSUM(NewId())) % 20,
n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM
sys.all_objects AS s1
CROSS JOIN
sys.all_objects AS s2
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
ON [dbo].[my_table] ([nki] ASC);
GO
Если я получу все записи, упорядоченные по [nki]
(некластеризованный индекс):
SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;
SQL Server Execution Times: CPU time = 266 ms, elapsed time = 493 ms
Оптимизатор выбирает кластеризованный индекс, а затем применяет алгоритм сортировки.
Но если я заставлю его использовать некластеризованный индекс:
SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS TIME OFF;
SQL Server Execution Times: CPU time = 311 ms, elapsed time = 188 ms
Затем он использует некластеризованный индекс с поиском ключа:
Очевидно, что если некластеризованный индекс преобразуется в покрывающий индекс:
CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
ON [dbo].[my_table] ([nki] ASC)
INCLUDE (id, foo, bar);
GO
Тогда он использует только этот индекс:
SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;
SQL Server Execution Times: CPU time = 32 ms, elapsed time = 106 ms
Вопрос
- Почему SQL Server использует кластеризованный индекс плюс алгоритм сортировки вместо некластеризованного индекса, даже если время выполнения в последнем случае увеличивается на 38%?
Ответы:
Поскольку SQL Server использует оптимизатор на основе затрат на основе статистики, а не информации времени выполнения.
Во время процесса оценки стоимости для этого запроса он действительно оценивает план поиска, но оценивает, что он потребует больше усилий. (Обратите внимание на «Оценочную стоимость поддерева» при наведении курсора на SELECT в плане выполнения). Это тоже не обязательно плохое предположение - на моей тестовой машине план поиска занимает в 6 раз больше процессора сортировки / сканирования.
Посмотрите на ответ Роба Фарли о том, почему SQL Server может стоить план поиска выше.
источник
Если бы вы сравнили количество операций чтения, требуемых в 100 000 поисков, с тем, что связано с выполнением сортировки, вы могли бы быстро получить представление о том, почему Query Optimizer считает, что сортировка CIX + будет лучшим выбором.
Выполнение Lookup заканчивается быстрее, потому что читаемые страницы находятся в памяти (даже если вы очищаете кеш, у вас много строк на страницу, поэтому вы читаете одни и те же страницы снова и снова, но с разной степенью фрагментации или другое давление памяти от другой деятельности, это может быть не так). На самом деле сортировка CIX + Sort не займет столько времени, но вы видите, что стоимость чтения не учитывает относительную дешевизну повторного посещения одних и тех же страниц.
источник
Я решил немного покопаться в этом вопросе и обнаружил некоторые интересные документы, рассказывающие о том, как и когда использовать, а может быть лучше, а не (форсировать) использование некластеризованного индекса.
Как следует из комментариев Джона Эйсбренера , одна из наиболее упоминаемых, даже в других блогах, это интересная статья Кимберли Л. Триппа:
но это не единственный, если вы заинтересованы, вы можете взглянуть на эти страницы:
Как видите, все они движутся вокруг концепции переломного момента .
Цитируется из статьи К.Л. Триппа
Когда SQL Server использует некластеризованный индекс в куче, в основном он получает список указателей на страницы базовой таблицы. Затем он использует эти указатели для извлечения строк с помощью ряда операций, называемых Row ID Lookups (RID). Это означает, что, по крайней мере, он будет использовать столько же чтений страниц, сколько возвращенных строк, а возможно, и больше. Этот процесс несколько похож на кластерный индекс в качестве базовой таблицы, с тем же результатом: больше операций чтения.
Но когда наступит этот переломный момент?
Конечно, как и большинство вещей в этой жизни, это зависит ...
Не серьезно, это происходит между 25% и 33% от числа страниц в таблице, в зависимости от того, сколько строк на странице. Но есть и другие факторы, которые вы должны учитывать:
Цитируется из статьи ITPRoToday
Теперь, если я выполню свои запросы снова, используя статистику ввода-вывода:
Второй запрос требует больше логических чтений, чем первый.
Должен ли я избегать некластеризованного индекса?
Нет, кластерный индекс может быть полезен, но стоит потратить время и приложить дополнительные усилия для анализа того, чего вы пытаетесь достичь с его помощью.
Цитируется из статьи К.Л. Триппа
источник