Descargo de responsabilidad : algunas de las cosas en esta respuesta pueden hacer que un DBA retroceda. Lo estoy abordando desde un punto de vista de rendimiento puro: cómo obtener Index Seeks cuando siempre obtiene Index Scans.
Con eso fuera del camino, aquí va.
Su consulta es lo que se conoce como "consulta del fregadero de la cocina", una consulta única destinada a satisfacer un rango de posibles condiciones de búsqueda. Si el usuario establece @status
un valor, desea filtrar ese estado. Si @status
es así NULL
, devuelve todos los estados, y así sucesivamente.
Esto introduce problemas con la indexación, pero no están relacionados con la sargabilidad, porque todas las condiciones de búsqueda son "iguales" a los criterios.
Esto es sargable:
WHERE [status]=@status
Esto no es modificable porque SQL Server necesita evaluar ISNULL([status], 0)
cada fila en lugar de buscar un solo valor en el índice:
WHERE ISNULL([status], 0)=@status
He recreado el problema del fregadero de la cocina de una forma más simple:
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);
Si intenta lo siguiente, obtendrá un Escaneo de índice, aunque A es la primera columna del índice:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
(@b IS NULL OR @b=B);
Esto, sin embargo, produce una búsqueda de índice:
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL;
Siempre que esté utilizando una cantidad manejable de parámetros (dos en su caso), probablemente podría realizar UNION
un montón de consultas de búsqueda, básicamente todas las permutaciones de los criterios de búsqueda. Si tiene tres criterios, esto se verá desordenado, con cuatro será completamente inmanejable. Has sido advertido.
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;
Sin (B, A)
embargo, para que el tercero de esos cuatro use un Index Seek, necesitará un segundo índice . Así es como se vería su consulta con estos cambios (incluida mi refactorización de la consulta para hacerla más legible).
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='');
... además, necesitará un índice adicional Employee
con las dos columnas de índice invertidas.
Para completar, debo mencionar que x=@x
implícitamente significa que x
no puede ser NULL
porque NULL
nunca es igual a NULL
. Eso simplifica un poco la consulta.
Y sí, la respuesta dinámica de SQL de Aaron Bertrand es una mejor opción en la mayoría de los casos (es decir, siempre que pueda vivir con las recompilaciones).
@Status
?