Давайте бросим один миллион строк во временную таблицу вместе с несколькими столбцами:
CREATE TABLE #174860 (
PK INT NOT NULL,
COL1 INT NOT NULL,
COL2 INT NOT NULL,
PRIMARY KEY (PK)
);
INSERT INTO #174860 WITH (TABLOCK)
SELECT RN
, RN % 1000
, RN % 10000
FROM
(
SELECT TOP 1000000 ROW_NUMBER () OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values v1,
master..spt_values v2
) t;
CREATE INDEX IX_174860_IX ON #174860 (COL1) INCLUDE (COL2);
Здесь у меня есть кластерный индекс (по умолчанию) для PKстолбца. Существует некластеризованный индекс COL1с ключевым столбцом COL1и включает в себя COL2.
Рассмотрим следующий запрос:
SELECT *
FROM #174860
WHERE PK >= 15000 AND PK < 15005
AND COL2 = 5000;
Здесь я не пользуюсь, BETWEENпотому что вокруг этого вопроса висит Аарон Бертран.
Как оптимизатор SQL Server должен выполнять этот запрос? Ну, я знаю, что фильтр PKвключит результирующий набор до пяти строк. Сервер SQL может использовать кластерный индекс для перехода к этим пяти строкам вместо чтения всех миллионов строк в таблице. Однако кластерный индекс имеет только столбец PK в качестве ключевого столбца. Как только строка будет считана в память, нам нужно применить фильтр COL2. Здесь PKпредикат поиска и COL2предикат.

SQL-сервер находит пять строк, используя предикат поиска, и дополнительно уменьшает эти пять строк до одной строки с обычным предикатом.
Если я определю кластерный индекс по-другому:
CREATE TABLE #174860 (
PK INT NOT NULL,
COL1 INT NOT NULL,
COL2 INT NOT NULL,
PRIMARY KEY (COL2, PK)
);
И выполнить тот же запрос, я получаю разные результаты:

В этом случае SQL Server может искать, используя оба столбца в WHEREпредложении. Ровно одна строка читается из таблицы, используя ключевые столбцы.
Для еще одного примера рассмотрим этот запрос:
SELECT *
FROM #174860
WHERE COL1 = 500
AND COL2 = 3545;
Индекс IX_174860_IX является индексом покрытия, поскольку он содержит все столбцы, необходимые для запроса. Однако только COL1ключевой столбец. SQL Server может выполнить поиск по этому столбцу, чтобы найти 1000 строк с соответствующим COL1значением. Он может дополнительно отфильтровать эти строки в COL2столбце, чтобы уменьшить конечный набор результатов до 0 строк.
