Bagaimana SQL Server menentukan urutan kolom kunci dalam permintaan indeks yang hilang?

32

Bagaimana SQL Server menentukan urutan kolom kunci dalam rekomendasi indeks yang hilang untuk rencana kueri?

Bryan Rebok
sumber

Jawaban:

44

Ketika SQL Server membuat rekomendasi indeks yang hilang untuk rencana kueri tertentu, itu memisahkan kolom kunci yang mungkin menjadi 2 kelompok. Set pertama berisi semua kolom yang direkomendasikan yang merupakan bagian dari predikat EQUALITY. Set kedua berisi semua kolom yang direkomendasikan yang merupakan bagian dari predikat INEQUALITY.

Dalam setiap set, kolom diurutkan berdasarkan posisi ordinal kolom, berdasarkan definisi tabel.

(Banyak terima kasih kepada Brent Ozar untuk membangun skrip repro terhadap database Stack Overflow untuk membuktikan ini!)

1. Buat 3 tabel identik , tetapi letakkan kolom mereka dalam urutan yang berbeda. (Alasannya di sini adalah untuk menggunakan berbagai nama kolom dan tipe data untuk menunjukkan bahwa itu tidak memengaruhi urutan kolom dalam rekomendasi indeks yang hilang.)

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. Isi tabel dengan data yang sama. Dapatkan 100.000 baris dari tabel Users dengan distribusi data dunia nyata.

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. Tulis kueri yang membutuhkan indeks. Mulai dengan 3 filter kesetaraan, pemfilteran untuk nilai yang tepat di ketiga bidang. Perhatikan bahwa semua 3 kueri memiliki bidang yang sama dalam urutan yang sama:

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

Ketiga tabel memiliki data yang persis sama, dan kueri identik. Satu-satunya perbedaan adalah urutan bidang - dan itulah perbedaan dalam permintaan indeks yang hilang juga:

Rencana eksekusi dengan 3 bidang kesetaraan

Dalam rencana eksekusi, urutan kolom dalam permintaan indeks yang hilang sama persis dengan urutan kolom dalam tabel. Misalnya, di dbo.NumberLetterDate, kolom angka adalah yang pertama, jadi itu juga yang pertama dalam permintaan indeks yang hilang, juga:

  • Pada dbo.NumberLetterDate, indeks yang hilang ada di fINT (angka), fLetter (nvarchar), fDate, urutan bidang yang sama dalam tabel
  • Pada dbo.LetterDateNumber, urutan indeks beralih ke fNVARCHAR, fDATE, fINT
  • Pada dbo.DateNumberLetter, urutan indeks beralih ke fDATE, fINT, fNVARCHAR

Untuk operasi satu-tabel seperti ini, urutan bidang indeks tampaknya tidak bergantung pada selektivitas, tipe data, atau posisi dalam kueri. (Saya serahkan kepada orang lain untuk membuktikan ini dengan pertanyaan & bergabung yang lebih kompleks.)

4. Campurkan filter ketimpangan. Pada bidang INT, misalnya, masukkan <> 100 sebagai filter:

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

Dalam rencana eksekusi, bidang persamaan pertama-tama, kemudian bidang ketidaksetaraan - jadi di sini, fINT muncul terakhir di ketiga permintaan indeks yang hilang karena ini merupakan pencarian ketidaksetaraan:

Rencana eksekusi dengan 2 persamaan, dan 1 pencarian ketidaksetaraan

5. Gunakan 3 filter ketimpangan. Gunakan pencarian yang sama untuk semua bidang (<>):

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

Karena tidak ada pencarian kesetaraan, ketiga bidang memiliki urutan prioritas yang sama dalam rekomendasi indeks yang hilang, dan sekarang kami kembali menyortir murni berdasarkan urutan bidang:

Rencana eksekusi dengan 3 pencarian ketidaksetaraan

Bryan Rebok
sumber