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 @status
un 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 UNION
un 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 Employee
con le due colonne dell'indice invertite.
Per completezza, dovrei dire che x=@x
implicitamente significa che x
non può essere NULL
perché NULL
non è 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
?