Відмова від відповідальності : Деякі речі в цій відповіді можуть змусити DBA здригнутися. Я підходжу до цього з точки зору чистої продуктивності - як отримати пошукові запити, коли ви завжди отримуєте індекси сканування.
Із цим не виходить, тут іде.
Ваш запит - це те, що відомо як "запит кухонної раковини" - один запит, який повинен забезпечити широкий спектр можливих умов пошуку. Якщо користувач встановив @statusзначення, потрібно фільтрувати цей статус. Якщо @statusє NULL, поверніть усі статуси тощо.
Це спричиняє проблеми з індексуванням, але вони не пов'язані із зручністю пошуку, оскільки всі ваші умови пошуку відповідають критеріям.
Це можна стверджувати:
WHERE [status]=@status
Це не піддається обробці, оскільки SQL Server повинен оцінювати ISNULL([status], 0)кожен рядок, а не шукати одне значення в індексі:
WHERE ISNULL([status], 0)=@status
Я відтворив кухонну мийку в більш простому вигляді:
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);
Якщо ви спробуєте наступне, ви отримаєте індексне сканування, навіть якщо A є першим стовпцем індексу:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
(@b IS NULL OR @b=B);
Це, однак, призводить до пошуку індексу:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL;
Поки ви використовуєте керовану кількість параметрів (у вашому випадку два), ви, ймовірно, можете просто UNIONкупувати запити - в основному всі перестановки критеріїв пошуку. Якщо у вас є три критерії, це буде виглядати безладно, з чотирма це буде абсолютно некеровано. Вас попередили.
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;
Для того, щоб третій із цих чотирьох використав Index Search, вам знадобиться другий індекс на (B, A). Ось як може виглядати ваш запит із цими змінами (включаючи рефакторинг запиту, щоб зробити його більш читабельним).
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='');
... плюс вам знадобиться додатковий індекс на Employeeдва стовпці індексу.
Для повноти я повинен зазначити, що x=@xнеявно означає, що xне може бути, NULLтому що NULLніколи не дорівнює NULL. Це трохи спрощує запит.
І так, динамічна відповідь SQL Аарона Бертран є кращим вибором у більшості випадків (тобто, коли можна жити з перекомпіляціями).
@Status?