Як SQL Server визначає порядок ключових стовпців у своїх відсутніх рекомендаціях щодо індексу для плану запитів?
Як SQL Server визначає порядок ключових стовпців у своїх відсутніх рекомендаціях щодо індексу для плану запитів?
Відповіді:
Коли SQL Server створює відсутність рекомендації щодо індексу для певного плану запитів, він розділяє можливі ключові стовпці на 2 групи. Перший набір містить усі рекомендовані стовпці, які є частиною предиката РІВНІСТЬ. Другий набір містить усі рекомендовані стовпці, що входять до предиката INEQUALITY.
У межах кожного набору стовпці упорядковуються порядковим положенням стовпців, виходячи з визначення таблиці.
(Велике спасибі Бренту Озару за те, що він створив сценарій повторної роботи проти бази даних переповнення стека, щоб довести це!)
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. Наповніть таблиці однаковими даними. Отримайте 100000 рядків з таблиці "Користувачі" за допомогою реального розповсюдження даних.
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 поля мають однаковий порядок пріоритетності у відсутній рекомендації щодо індексу, і тепер ми повернулися до сортування виключно за порядком полів: