Давайте бросим один миллион строк во временную таблицу вместе с несколькими столбцами:
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 строк.