Tuyên bố miễn trừ trách nhiệm : Một số nội dung trong câu trả lời này có thể khiến DBA nao núng. Tôi đang tiếp cận nó từ quan điểm hiệu suất thuần túy - làm thế nào để có được Tìm kiếm chỉ mục khi bạn luôn nhận được Quét chỉ mục.
Với điều đó ra khỏi đường đi, ở đây đi.
Truy vấn của bạn là những gì được gọi là "truy vấn bồn rửa nhà bếp" - một truy vấn duy nhất có nghĩa là để phục vụ cho một loạt các điều kiện tìm kiếm có thể. Nếu người dùng đặt @status
thành một giá trị, bạn muốn lọc theo trạng thái đó. Nếu @status
là NULL
, trả lại tất cả các trạng thái, và như vậy.
Điều này đưa ra các vấn đề với việc lập chỉ mục, nhưng chúng không liên quan đến tính khả dụng, bởi vì tất cả các điều kiện tìm kiếm của bạn đều "bằng" tiêu chí.
Đây là sargable:
WHERE [status]=@status
Điều này không thể thực hiện được vì SQL Server cần đánh giá ISNULL([status], 0)
cho mỗi hàng thay vì tìm kiếm một giá trị duy nhất trong chỉ mục:
WHERE ISNULL([status], 0)=@status
Tôi đã tạo lại vấn đề bồn rửa nhà bếp ở dạng đơn giản hơn:
CREATE TABLE #work (
A int NOT NULL,
B int NOT NULL
);
CREATE UNIQUE INDEX #work_ix1 ON #work (A, B);
INSERT INTO #work (A, B)
VALUES (1, 1), (2, 1),
(3, 1), (4, 1),
(5, 2), (6, 2),
(7, 2), (8, 3),
(9, 3), (10, 3);
Nếu bạn thử các cách sau, bạn sẽ nhận được Quét chỉ mục, mặc dù A là cột đầu tiên của chỉ mục:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
(@b IS NULL OR @b=B);
Tuy nhiên, điều này tạo ra Chỉ số Tìm kiếm:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL;
Miễn là bạn đang sử dụng một lượng tham số có thể quản lý (hai trong trường hợp của bạn), bạn có thể chỉ cần UNION
một loạt các truy vấn tìm kiếm - về cơ bản là tất cả các hoán vị của tiêu chí tìm kiếm. Nếu bạn có ba tiêu chí, điều này sẽ trông lộn xộn, với bốn tiêu chí sẽ hoàn toàn không thể quản lý được. Mày đã được cảnh báo.
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL
UNION ALL
SELECT *
FROM #work
WHERE @a=A AND
@b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
@b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
@b IS NULL;
Đối với người thứ ba trong số bốn người đó sử dụng Chỉ mục Tìm kiếm, bạn sẽ cần một chỉ mục thứ hai (B, A)
. Đây là cách truy vấn của bạn có thể trông như thế nào với những thay đổi này (bao gồm cả việc tái cấu trúc truy vấn của tôi để làm cho nó dễ đọc hơn).
DECLARE @Status int = NULL,
@IsUserGotAnActiveDirectoryUser bit = NULL;
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser IS NULL
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='')
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser IS NULL
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='');
... cộng với bạn sẽ cần một chỉ mục bổ sung Employee
với hai cột chỉ mục được đảo ngược.
Để cho đầy đủ, tôi nên đề cập rằng điều đó x=@x
có nghĩa là x
không thể NULL
bởi vì NULL
không bao giờ bằng NULL
. Điều đó đơn giản hóa các truy vấn một chút.
Và, vâng, câu trả lời SQL động của Aaron Bertrand là một lựa chọn tốt hơn trong hầu hết các trường hợp (tức là bất cứ khi nào bạn có thể sống với các biên dịch lại).
@Status
?