Làm thế nào để SQL Server xác định thứ tự của các cột chính trong các đề xuất chỉ mục bị thiếu cho kế hoạch truy vấn?
Làm thế nào để SQL Server xác định thứ tự của các cột chính trong các đề xuất chỉ mục bị thiếu cho kế hoạch truy vấn?
Câu trả lời:
Khi SQL Server tạo một đề xuất chỉ mục bị thiếu cho một kế hoạch truy vấn cụ thể, nó sẽ tách các cột khóa có thể thành 2 nhóm. Tập đầu tiên chứa tất cả các cột được đề xuất là một phần của vị từ THIẾT BỊ. Tập thứ hai chứa tất cả các cột được đề xuất là một phần của vị từ INEQUALITY.
Trong mỗi bộ, các cột được sắp xếp theo vị trí thứ tự của các cột, dựa trên định nghĩa bảng.
(Rất cám ơn Brent Ozar đã xây dựng tập lệnh repro dựa trên cơ sở dữ liệu Stack Overflow để chứng minh điều này!)
1. Tạo 3 bảng giống nhau , nhưng đặt các cột của chúng theo thứ tự khác nhau. (Lý do ở đây là sử dụng nhiều tên cột và kiểu dữ liệu khác nhau để chỉ ra rằng điều đó không ảnh hưởng đến thứ tự cột trong đề xuất chỉ mục bị thiếu.)
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. Điền vào các bảng có cùng dữ liệu. Nhận 100.000 hàng từ bảng Người dùng với phân phối dữ liệu trong thế giới thực.
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. Viết một truy vấn cần một chỉ mục. Bắt đầu với 3 bộ lọc bằng nhau, lọc cho một giá trị chính xác trong cả 3 trường. Lưu ý rằng cả 3 truy vấn đều có cùng các trường theo cùng một thứ tự:
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
Tất cả ba bảng có cùng một dữ liệu và các truy vấn giống hệt nhau. Sự khác biệt duy nhất là thứ tự trường - và đó cũng là sự khác biệt trong các yêu cầu chỉ mục bị thiếu của chúng tôi:
Trong các kế hoạch thực hiện, thứ tự cột trong yêu cầu chỉ mục bị thiếu chính xác khớp với thứ tự cột trong bảng. Ví dụ: trong dbo.NumberLetterDate, cột số là đầu tiên, do đó, nó cũng là đầu tiên trong yêu cầu chỉ mục bị thiếu:
Đối với thao tác một bảng như thế này, thứ tự trường chỉ mục dường như không phụ thuộc vào độ chọn lọc, kiểu dữ liệu hoặc vị trí trong truy vấn. (Tôi để nó cho những người khác để chứng minh điều này với các truy vấn và tham gia phức tạp hơn.)
4. Trộn trong bộ lọc bất đẳng thức. Ví dụ, trên trường INT, đặt <> 100 làm bộ lọc:
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
Trong các kế hoạch thực hiện, các trường bình đẳng đi trước, sau đó là các trường bất đẳng thức - vì vậy ở đây, fINT xuất hiện cuối cùng trong cả 3 yêu cầu chỉ mục bị thiếu vì đó là tìm kiếm bất đẳng thức:
5. Sử dụng 3 bộ lọc bất đẳng thức. Sử dụng cùng một tìm kiếm cho tất cả các trường (<>):
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
Vì không có tìm kiếm bình đẳng, cả 3 trường đều có cùng thứ tự ưu tiên trong đề xuất chỉ mục bị thiếu và bây giờ chúng tôi quay lại sắp xếp hoàn toàn theo thứ tự trường: