Oświadczenie : Niektóre elementy w tej odpowiedzi mogą powodować wzdrygnięcie DBA. Podchodzę do tego z czystego punktu widzenia wydajności - jak uzyskać indeksowanie, gdy zawsze dostajesz indeksy.
Z tym nie ma mowy, oto idzie.
Twoje zapytanie to tak zwane „zapytanie dotyczące zlewu kuchennego” - pojedyncze zapytanie przeznaczone do spełnienia szeregu możliwych warunków wyszukiwania. Jeśli użytkownik ustawi @status
wartość, chcesz filtrować według tego stanu. Jeśli @status
tak NULL
, zwróć wszystkie statusy i tak dalej.
Powoduje to problemy z indeksowaniem, ale nie są one związane z możliwościami wyszukiwania, ponieważ wszystkie warunki wyszukiwania są „równe” kryteriom.
To jest do sargable:
WHERE [status]=@status
Nie jest to możliwe do sargowania, ponieważ SQL Server musi oceniać ISNULL([status], 0)
dla każdego wiersza zamiast szukać pojedynczej wartości w indeksie:
WHERE ISNULL([status], 0)=@status
Odtworzyłem problem zlewu kuchennego w prostszej formie:
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);
Jeśli spróbujesz wykonać następujące czynności, otrzymasz skanowanie indeksu, mimo że A jest pierwszą kolumną indeksu:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
(@b IS NULL OR @b=B);
Powoduje to jednak wyszukiwanie indeksu:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL;
Tak długo, jak używasz możliwej do zarządzania liczby parametrów (w twoim przypadku dwóch), prawdopodobnie możesz po prostu UNION
wyszukać kilka zapytań - w zasadzie wszystkie kombinacje kryteriów wyszukiwania. Jeśli masz trzy kryteria, będzie to bałagan, z czterema będzie to całkowicie niemożliwe do zarządzania. Zostałeś ostrzeżony.
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;
Jednak, aby trzeci z tych czterech mógł korzystać z funkcji wyszukiwania indeksu, będziesz potrzebować drugiego indeksu (B, A)
. Oto, w jaki sposób zapytanie może wyglądać z tymi zmianami (w tym przez przeredagowanie zapytania, aby było bardziej czytelne).
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 będziesz potrzebować dodatkowego indeksu przy Employee
odwróconych dwóch kolumnach indeksu.
Dla kompletności powinienem wspomnieć, że x=@x
domyślnie oznacza to, że x
nie może być, NULL
ponieważ NULL
nigdy nie jest równy NULL
. To trochę upraszcza zapytanie.
I tak, dynamiczna odpowiedź SQL Aarona Bertranda jest lepszym wyborem w większości przypadków (tj. Ilekroć możesz żyć z rekompilacjami).
@Status
?