Necesito optimizar una SELECT
declaración, pero SQL Server siempre realiza una exploración de índice en lugar de una búsqueda. Esta es la consulta que, por supuesto, se encuentra en un procedimiento almacenado:
CREATE PROCEDURE dbo.something
@Status INT = NULL,
@IsUserGotAnActiveDirectoryUser BIT = NULL
AS
SELECT [IdNumber], [Code], [Status], [Sex],
[FirstName], [LastName], [Profession],
[BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE (@Status IS NULL OR [Status] = @Status)
AND
(
@IsUserGotAnActiveDirectoryUser IS NULL
OR
(
@IsUserGotAnActiveDirectoryUser IS NOT NULL AND
(
@IsUserGotAnActiveDirectoryUser = 1 AND ActiveDirectoryUser <> ''
)
OR
(
@IsUserGotAnActiveDirectoryUser = 0 AND ActiveDirectoryUser = ''
)
)
)
Y este es el índice:
CREATE INDEX not_relevent ON dbo.Employee
(
[Status] DESC,
[ActiveDirectoryUser] ASC
)
INCLUDE (...all the other columns in the table...);
El plan:
¿Por qué SQL Server eligió un escaneo? ¿Cómo puedo arreglarlo?
Definiciones de columna:
[Status] int NOT NULL
[ActiveDirectoryUser] VARCHAR(50) NOT NULL
Los parámetros de estado pueden ser:
NULL: all status,
1: Status= 1 (Active employees)
2: Status = 2 (Inactive employees)
IsUserGotAnActiveDirectoryUser puede ser:
NULL: All employees
0: ActiveDirectoryUser is empty for that employee
1: ActiveDirectoryUser got a valid value (not null and not empty)
sql-server
sql-server-2012
index
optimization
Bestter
fuente
fuente
@Status
?Status DESC
? ¿Para cuántos valores hayStatus
, para qué sirven (si el número es pequeño), y cada valor se representa aproximadamente por igual?SELECT TOP (20) [Status], c = COUNT(*) FROM dbo.Employee GROUP BY [Status] ORDER BY c DESC;
Respuestas:
No creo que el escaneo sea causado por una búsqueda de una cadena vacía (y si bien podría agregar un índice filtrado para ese caso, solo ayudará a variaciones muy específicas de la consulta). Es más probable que sea víctima de la detección de parámetros y un plan único que no esté optimizado para todas las diversas combinaciones de parámetros (y valores de parámetros) que proporcionará a esta consulta.
Yo llamo a esto el procedimiento de "fregadero de la cocina" , porque espera que una consulta proporcione todas las cosas, incluido el fregadero de la cocina.
Tengo un video sobre mi solución para esto aquí , pero esencialmente, la mejor experiencia que tengo para tales consultas es:
OPTION (RECOMPILE)
: esto evita que valores de parámetros específicos obliguen al tipo de plan incorrecto, especialmente útil cuando hay sesgo de datos, estadísticas erróneas o cuando la primera ejecución de una declaración usa un valor atípico que conducirá a un plan diferente que más tarde y más frecuente ejecucionesoptimize for ad hoc workloads
: esto evita que las variaciones de consulta que solo se usan una vez contaminen la memoria caché de su plan.Habilite la optimización para cargas de trabajo ad hoc:
Cambia tu procedimiento:
Una vez que tenga una carga de trabajo basada en ese conjunto de consultas que puede monitorear, puede analizar las ejecuciones y ver cuáles se beneficiarían más de índices adicionales o diferentes: puede hacerlo desde una variedad de ángulos, desde una simple "combinación de los parámetros se proporcionan con mayor frecuencia? " a "¿qué consultas individuales tienen los tiempos de ejecución más largos?" No podemos responder a esas preguntas basándonos solo en su código, solo podemos sugerir que es NULL, luego no es posible buscar ese índice no agrupado. Entonces, para aquellos casos en los que a los usuarios no les importa el estado, obtendrá un escaneo, a menos que tenga un índice que atienda a las otras cláusulas (pero dicho índice tampoco será útil, dada su lógica de consulta actual - una cadena vacía o no vacía no es exactamente selectiva). cualquier índice solo será útil para un subconjunto de todas las combinaciones de parámetros posibles que está intentando admitir. Por ejemplo, si
@Status
En este caso, dependiendo del conjunto de
Status
valores posibles y de la distribución de esos valores,OPTION (RECOMPILE)
puede que no sea necesario. Pero si tiene algunos valores que arrojarán 100 filas y algunos valores que arrojarán cientos de miles, es posible que lo desee allí (incluso al costo de la CPU, que debería ser marginal dada la complejidad de esta consulta), para que pueda get busca en la mayor cantidad de casos posible. Si el rango de valores es lo suficientemente finito, incluso podría hacer algo complicado con el SQL dinámico, donde dice "Tengo este valor muy selectivo para@Status
, así que cuando se pasa ese valor específico, haga esta ligera alteración en el texto de la consulta para que esto se considera una consulta diferente y está optimizado para ese valor de parámetro ".fuente
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:
Esto no es modificable porque SQL Server necesita evaluar
ISNULL([status], 0)
cada fila en lugar de buscar un solo valor en el índice:He recreado el problema del fregadero de la cocina de una forma más simple:
Si intenta lo siguiente, obtendrá un Escaneo de índice, aunque A es la primera columna del índice:
Esto, sin embargo, produce una búsqueda de índice:
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.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).... además, necesitará un índice adicional
Employee
con las dos columnas de índice invertidas.Para completar, debo mencionar que
x=@x
implícitamente significa quex
no puede serNULL
porqueNULL
nunca es igual aNULL
. 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).
fuente
Su pregunta básica parece ser "Por qué" y creo que puede encontrar la respuesta sobre el minuto 55 más o menos de esta gran presentación de Adam Machanic en TechEd hace unos años.
Menciono los 5 minutos en el minuto 55, pero toda la presentación vale la pena. Si observa el plan de consulta para su consulta, estoy seguro de que encontrará que tiene predicados residuales para la búsqueda. Básicamente, SQL no puede "ver" todas las partes del índice porque algunas de ellas están ocultas por las desigualdades y otras condiciones. El resultado es una exploración de índice para un superconjunto basado en el predicado. Ese resultado se pone en cola y luego se vuelve a escanear utilizando el predicado residual.
Verifique las propiedades del Operador de escaneo (F4) y vea si tiene tanto "Buscar predicado" como "Predicar" en la lista de propiedades.
Como otros han indicado, la consulta es difícil de indexar tal como está. He estado trabajando en muchos similares recientemente y cada uno ha requerido una solución diferente. :(
fuente
Antes de preguntarnos si se prefiere la búsqueda de índice sobre la exploración de índice, una regla general es verificar cuántas filas se devuelven frente a las filas totales de la tabla subyacente. Por ejemplo, si espera que su consulta devuelva 10 filas de 1 millón de filas, entonces la búsqueda de índice probablemente sea más preferible que la exploración de índice. Sin embargo, si se van a devolver unos pocos miles de filas (o más) de la consulta, entonces la búsqueda de índice NO necesariamente se preferirá.
Su consulta no es compleja, por lo que si puede publicar un plan de ejecución, podemos tener mejores ideas para ayudarlo.
fuente
este es solo el formato original
Esta es la revisión: no estoy 100% seguro, pero (tal vez) lo intente,
incluso uno O probablemente sea un problema que
se rompería en ActiveDirectoryUser nulo
fuente