Как SQL Server определяет порядок ключевых столбцов в отсутствующих запросах индекса?

32

Как SQL Server определяет порядок расположения ключевых столбцов в отсутствующих рекомендациях по индексам для плана запроса?

Брайан Ребок
источник

Ответы:

44

Когда 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

Все три таблицы имеют абсолютно одинаковые данные, а запросы идентичны. Единственное отличие - это порядок полей - и в этом также есть разница в наших отсутствующих запросах индекса:

Планы выполнения с 3 полями равенства

В планах выполнения порядок столбцов в отсутствующем запросе индекса точно соответствует порядку столбцов в таблице. Например, в dbo.NumberLetterDate столбец числа является первым, поэтому он также является первым в отсутствующем запросе индекса:

  • В dbo.NumberLetterDate отсутствует индекс fINT (число), fLetter (nvarchar), fDate, тот же порядок полей в таблице
  • В dbo.LetterDateNumber порядок индексов переключается на fNVARCHAR, fDATE, fINT
  • В dbo.DateNumberLetter порядок индексов переключается на fDATE, fINT, fNVARCHAR

Для такой операции с одной таблицей порядок полей индекса, по-видимому, не зависит от селективности, типа данных или позиции в запросе. (Я оставляю это другим людям, чтобы доказать это с более сложными запросами и объединениями.)

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 пропущенных запросах индекса, потому что это поиск неравенства:

Планы выполнения с 2 равенством и 1 поиском неравенства

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 поля имеют одинаковый порядок приоритетов в рекомендации отсутствующего индекса, и теперь мы вернулись к сортировке исключительно по порядку полей:

Планы выполнения с 3 поисками неравенства

Брайан Ребок
источник