Disclaimer : alcune delle cose in questa risposta possono far sussultare il DBA. Mi sto avvicinando da un punto di vista puramente prestazionale: come ottenere Index Seeks quando ottieni sempre Scansioni Index.
Con quello fuori mano, ecco qui.
La tua query è nota come "query del lavello della cucina", una singola query pensata per soddisfare una serie di possibili condizioni di ricerca. Se l'utente imposta @statusun valore, si desidera filtrare su quello stato. Se @statusè NULL, restituire tutti gli stati, e così via.
Ciò introduce problemi con l'indicizzazione, ma non sono correlati alla sargability, poiché tutte le condizioni di ricerca sono criteri "uguali a".
Questo è sargable:
WHERE [status]=@status
Non è possibile eseguire il sarging in quanto SQL Server deve valutare ISNULL([status], 0)per ogni riga anziché cercare un singolo valore nell'indice:
WHERE ISNULL([status], 0)=@status
Ho ricreato il problema del lavello della cucina in una forma più semplice:
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);
Se provi quanto segue, otterrai una scansione dell'indice, anche se A è la prima colonna dell'indice:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
(@b IS NULL OR @b=B);
Questo, tuttavia, produce una ricerca indice:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL;
Finché stai usando una quantità gestibile di parametri (due nel tuo caso), potresti probabilmente solo UNIONun mucchio di query di ricerca - praticamente tutte le permutazioni dei criteri di ricerca. Se hai tre criteri, questo sembrerà disordinato, con quattro sarà completamente ingestibile. Sei stato avvisato.
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;
Affinché la terza di quelle quattro utilizzi una ricerca indice, avrai bisogno di un secondo indice (B, A). Ecco come potrebbe apparire la tua query con queste modifiche (incluso il mio refactoring della query per renderla più leggibile).
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='');
... inoltre avrai bisogno di un indice aggiuntivo attivo Employeecon le due colonne dell'indice invertite.
Per completezza, dovrei dire che x=@ximplicitamente significa che xnon può essere NULLperché NULLnon è mai uguale a NULL. Ciò semplifica un po 'la query.
E, sì, la risposta dinamica SQL di Aaron Bertrand è una scelta migliore nella maggior parte dei casi (ovvero ogni volta che puoi convivere con i ricompilamenti).
@Status?