Как SQL Server определяет порядок расположения ключевых столбцов в отсутствующих рекомендациях по индексам для плана запроса?
Как SQL Server определяет порядок расположения ключевых столбцов в отсутствующих рекомендациях по индексам для плана запроса?
Ответы:
Когда SQL Server создает отсутствующую рекомендацию по индексу для конкретного плана запроса, он разделяет возможные ключевые столбцы на 2 группы. Первый набор содержит все рекомендуемые столбцы, которые являются частью предиката EQUALITY. Второй набор содержит все рекомендуемые столбцы, которые являются частью предиката INEQUALITY.
Внутри каждого набора столбцы упорядочены по порядковому положению столбцов на основе определения таблицы.
(Большое спасибо Бренту Озару за создание сценария воспроизведения для базы данных Stack Overflow, чтобы доказать это!)
1. Создайте 3 одинаковые таблицы , но расположите их столбцы в другом порядке. (Причина здесь в том, чтобы использовать различные имена столбцов и типы данных, чтобы показать, что это не влияет на порядок столбцов в рекомендации по отсутствующему индексу.)
CREATE TABLE dbo.NumberLetterDate (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
fINT INT, fNVARCHAR NVARCHAR(40), fDATE DATETIME, AboutMe NVARCHAR(MAX));
GO
CREATE TABLE dbo.LetterDateNumber (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
fNVARCHAR NVARCHAR(40), fDATE DATETIME, fINT INT, AboutMe NVARCHAR(MAX));
GO
CREATE TABLE dbo.DateNumberLetter (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
fDATE DATETIME, fINT INT, fNVARCHAR NVARCHAR(40), AboutMe NVARCHAR(MAX));
GO
2. Заполните таблицы одинаковыми данными. Получите 100 000 строк из таблицы Users с помощью реального распределения данных.
INSERT INTO dbo.NumberLetterDate(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
FROM dbo.Users WITH (NOLOCK)
ORDER BY Id;
GO
INSERT INTO dbo.LetterDateNumber(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
FROM dbo.Users WITH (NOLOCK)
ORDER BY Id;
GO
INSERT INTO dbo.DateNumberLetter(fINT, fNVARCHAR, fDATE, AboutMe)
SELECT TOP 100000 Age, DisplayName, LastAccessDate, AboutMe
FROM dbo.Users WITH (NOLOCK)
ORDER BY Id;
GO
3. Напишите запрос, который нуждается в индексе. Начните с 3 фильтров равенства, фильтруя точное значение во всех 3 полях. Обратите внимание, что все 3 запроса имеют одинаковые поля в одинаковом порядке:
SELECT ID
FROM dbo.NumberLetterDate
WHERE fINT = 100
AND fNVARCHAR = 'Brent Ozar'
AND fDATE = '2018/01/01'
AND 1 = (SELECT 1);
SELECT ID
FROM dbo.LetterDateNumber
WHERE fINT = 100
AND fNVARCHAR = 'Brent Ozar'
AND fDATE = '2018/01/01'
AND 1 = (SELECT 1);
SELECT ID
FROM dbo.DateNumberLetter
WHERE fINT = 100
AND fNVARCHAR = 'Brent Ozar'
AND fDATE = '2018/01/01'
AND 1 = (SELECT 1);
GO
Все три таблицы имеют абсолютно одинаковые данные, а запросы идентичны. Единственное отличие - это порядок полей - и в этом также есть разница в наших отсутствующих запросах индекса:
В планах выполнения порядок столбцов в отсутствующем запросе индекса точно соответствует порядку столбцов в таблице. Например, в dbo.NumberLetterDate столбец числа является первым, поэтому он также является первым в отсутствующем запросе индекса:
Для такой операции с одной таблицей порядок полей индекса, по-видимому, не зависит от селективности, типа данных или позиции в запросе. (Я оставляю это другим людям, чтобы доказать это с более сложными запросами и объединениями.)
4. Смешайте в фильтре неравенства. Например, в поле INT введите <> 100 в качестве фильтра:
SELECT ID
FROM dbo.NumberLetterDate
WHERE fINT <> 100
AND fNVARCHAR = 'Brent Ozar'
AND fDATE = '2018/01/01'
AND 1 = (SELECT 1);
SELECT ID
FROM dbo.LetterDateNumber
WHERE fINT <> 100
AND fNVARCHAR = 'Brent Ozar'
AND fDATE = '2018/01/01'
AND 1 = (SELECT 1);
SELECT ID
FROM dbo.DateNumberLetter
WHERE fINT <> 100
AND fNVARCHAR = 'Brent Ozar'
AND fDATE = '2018/01/01'
AND 1 = (SELECT 1);
GO
В планах выполнения сначала идут поля равенства, затем поля неравенства - так что здесь, fINT отображается последним во всех 3 пропущенных запросах индекса, потому что это поиск неравенства:
5. Используйте 3 фильтра неравенства. Используйте одинаковый поиск для всех полей (<>):
SELECT ID
FROM dbo.NumberLetterDate
WHERE fINT <> 100
AND fNVARCHAR <> 'Brent Ozar'
AND fDATE <> '2018/01/01'
AND 1 = (SELECT 1);
SELECT ID
FROM dbo.LetterDateNumber
WHERE fINT <> 100
AND fNVARCHAR <> 'Brent Ozar'
AND fDATE <> '2018/01/01'
AND 1 = (SELECT 1);
SELECT ID
FROM dbo.DateNumberLetter
WHERE fINT <> 100
AND fNVARCHAR <> 'Brent Ozar'
AND fDATE <> '2018/01/01'
AND 1 = (SELECT 1);
GO
Поскольку поиск по равенству отсутствует, все 3 поля имеют одинаковый порядок приоритетов в рекомендации отсутствующего индекса, и теперь мы вернулись к сортировке исключительно по порядку полей: