Отказ от ответственности : некоторые вещи в этом ответе могут заставить ДБА вздрогнуть. Я подхожу к этому с чистой точки зрения производительности - как получить индексный поиск, когда вы всегда получаете индексное сканирование.
С этим из пути, здесь идет.
Ваш запрос - это так называемый «запрос кухонной раковины» - один запрос, предназначенный для удовлетворения целого ряда возможных условий поиска. Если пользователь устанавливает @status
значение, вы хотите фильтровать это состояние. Если @status
есть NULL
, вернуть все статусы и так далее.
Это создает проблемы с индексацией, но они не связаны с гибкостью, потому что все ваши условия поиска соответствуют критериям.
Это саркастично
WHERE [status]=@status
Это не sargable , поскольку 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;
Однако для того, чтобы третий из этих четырех использовал поиск по индексу, вам понадобится второй индекс (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
?