Penafian : Beberapa hal dalam jawaban ini dapat membuat DBA tersentak. Saya mendekatinya dari sudut pandang kinerja murni - cara mendapatkan Indeks Mencari ketika Anda selalu mendapatkan Indeks Scan.
Dengan hal itu, ini dia.
Permintaan Anda adalah apa yang dikenal sebagai "permintaan wastafel dapur" - satu permintaan yang dimaksudkan untuk memenuhi berbagai kondisi pencarian yang memungkinkan. Jika pengguna menetapkan @status
ke nilai, Anda ingin memfilter pada status itu. Jika @status
adalah NULL
, mengembalikan semua status, dan sebagainya.
Ini menimbulkan masalah dengan pengindeksan, tetapi mereka tidak terkait dengan sargability, karena semua kondisi pencarian Anda adalah kriteria "sama dengan".
Ini masuk akal:
WHERE [status]=@status
Ini tidak masuk akal karena SQL Server perlu mengevaluasi ISNULL([status], 0)
untuk setiap baris alih-alih mencari nilai tunggal dalam indeks:
WHERE ISNULL([status], 0)=@status
Saya telah menciptakan kembali masalah wastafel dapur dalam bentuk yang lebih sederhana:
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);
Jika Anda mencoba yang berikut ini, Anda akan mendapatkan Pemindaian Indeks, meskipun A adalah kolom pertama dari indeks:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
(@b IS NULL OR @b=B);
Ini, bagaimanapun, menghasilkan Indeks Mencari:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL;
Selama Anda menggunakan jumlah parameter yang dapat dikelola (dua dalam kasus Anda), Anda mungkin bisa hanya UNION
sekelompok permintaan pencarian - pada dasarnya semua permutasi kriteria pencarian. Jika Anda memiliki tiga kriteria, ini akan terlihat berantakan, dengan empat kriteria itu akan sepenuhnya tidak dapat dikelola. Anda sudah diperingatkan.
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;
Untuk yang ketiga dari keempat untuk menggunakan Indeks Mencari, Anda akan memerlukan indeks kedua (B, A)
. Begini cara kueri Anda terlihat dengan perubahan-perubahan ini (termasuk refactoring saya atas kueri agar lebih mudah dibaca).
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='');
... plus Anda akan memerlukan indeks tambahan Employee
dengan dua kolom indeks dibalik.
Untuk kelengkapan, saya harus menyebutkan bahwa x=@x
secara implisit berarti itu x
tidak mungkin NULL
karena NULL
tidak pernah sama dengan NULL
. Itu sedikit menyederhanakan kueri.
Dan, ya, jawaban SQL dinamis Aaron Bertrand adalah pilihan yang lebih baik dalam kebanyakan kasus (yaitu kapan pun Anda dapat hidup dengan kompilasi).
@Status
?