La consulta es
SELECT SUM(Amount) AS SummaryTotal
FROM PDetail WITH(NOLOCK)
WHERE ClientID = @merchid
AND PostedDate BETWEEN @datebegin AND @dateend
La tabla contiene 103,129,000 filas.
El plan rápido busca ClientId con un predicado residual en la fecha, pero necesita realizar 96 búsquedas para recuperar el Amount
. La <ParameterList>
sección del plan es la siguiente.
<ParameterList>
<ColumnReference Column="@dateend"
ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
<ColumnReference Column="@datebegin"
ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
<ColumnReference Column="@merchid"
ParameterRuntimeValue="(78155)" />
</ParameterList>
El plan lento busca por fecha y tiene búsquedas para evaluar el predicado residual en ClientId y recuperar la cantidad (Estimado 1 vs 7,388,383 real). La <ParameterList>
sección es
<ParameterList>
<ColumnReference Column="@EndDate"
ParameterCompiledValue="'2013-02-01 23:59:00.000'"
ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
<ColumnReference Column="@BeginDate"
ParameterCompiledValue="'2013-01-01 00:00:00.000'"
ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
<ColumnReference Column="@ClientID"
ParameterCompiledValue="(78155)"
ParameterRuntimeValue="(78155)" />
</ParameterList>
En este segundo caso, el noParameterCompiledValue
está vacío. SQL Server olfateó con éxito los valores utilizados en la consulta.
El libro "Solución de problemas prácticos de SQL Server 2005" tiene esto que decir sobre el uso de variables locales
El uso de variables locales para vencer el rastreo de parámetros es un truco bastante común, pero las sugerencias OPTION (RECOMPILE)
y OPTION (OPTIMIZE FOR)
... generalmente son soluciones más elegantes y ligeramente menos riesgosas
Nota
En SQL Server 2005, la compilación de nivel de instrucción permite que la compilación de una instrucción individual en un procedimiento almacenado se difiera hasta justo antes de la primera ejecución de la consulta. Para entonces, se conocería el valor de la variable local. Teóricamente, SQL Server podría aprovechar esto para detectar los valores de las variables locales de la misma manera que los parámetros. Sin embargo, debido a que era común usar variables locales para vencer el rastreo de parámetros en SQL Server 7.0 y SQL Server 2000+, el rastreo de variables locales no estaba habilitado en SQL Server 2005. Sin embargo, puede habilitarse en una versión futura de SQL Server, lo cual es una buena opción. razón para usar una de las otras opciones descritas en este capítulo si tiene una opción.
A partir de una prueba rápida de este fin, el comportamiento descrito anteriormente sigue siendo el mismo en 2008 y 2012 y las variables no se analizan para la compilación diferida, sino solo cuando OPTION RECOMPILE
se utiliza una pista explícita .
DECLARE @N INT = 0
CREATE TABLE #T ( I INT );
/*Reference to #T means this statement is subject to deferred compile*/
SELECT *
FROM master..spt_values
WHERE number = @N
AND EXISTS(SELECT COUNT(*) FROM #T)
SELECT *
FROM master..spt_values
WHERE number = @N
OPTION (RECOMPILE)
DROP TABLE #T
A pesar de la compilación diferida, la variable no se detecta y el recuento de filas estimado es inexacto
Así que supongo que el plan lento se relaciona con una versión parametrizada de la consulta.
El valor ParameterCompiledValue
es igual a ParameterRuntimeValue
para todos los parámetros, por lo que este no es un análisis típico de parámetros (donde el plan se compiló para un conjunto de valores y luego se ejecutó para otro conjunto de valores).
El problema es que el plan que se compila para los valores correctos de los parámetros es inapropiado.
Es probable que tenga problemas con las fechas ascendentes descritas aquí y aquí . Para una tabla con 100 millones de filas, debe insertar (o modificar) 20 millones antes de que SQL Server actualice automáticamente las estadísticas por usted. Parece que la última vez que se actualizaron, las filas cero coincidieron con el intervalo de fechas en la consulta, pero ahora 7 millones sí.
Puede programar actualizaciones de estadísticas más frecuentes, considerar marcas de seguimiento 2389 - 90
o usarlas OPTIMIZE FOR UKNOWN
para que simplemente recurra a conjeturas en lugar de poder usar las estadísticas engañosas actualmente en la datetime
columna.
Esto podría no ser necesario en la próxima versión de SQL Server (después de 2012). Un elemento relacionado de Connect contiene la respuesta intrigante
Publicado por Microsoft el 8/28/2012 a las 1:35 p. M.
Hemos realizado una mejora en la estimación de la cardinalidad para la próxima versión principal que esencialmente soluciona esto. Estén atentos para más detalles una vez que salgan nuestros avances. Eric
Benjamin Nevarez analiza esta mejora de 2014 hacia el final del artículo:
Una primera mirada al nuevo estimador de cardinalidad de SQL Server .
Parece que el nuevo estimador de cardinalidad retrocederá y usará la densidad promedio en este caso en lugar de dar la estimación de 1 fila.
Algunos detalles adicionales sobre el estimador de cardinalidad de 2014 y el problema clave ascendente aquí:
Nueva funcionalidad en SQL Server 2014 - Parte 2 - Nueva estimación de cardinalidad
Yo tenía el mismo problema en un procedimiento almacenado se convirtió lento, y
OPTIMIZE FOR UNKNOWN
yRECOMPILE
sugerencias de consulta resuelto la lentitud y aceleró el tiempo de ejecución. Sin embargo, los siguientes dos métodos no afectaron la lentitud del procedimiento almacenado: (i) Borrar el caché (ii) usando WITH RECOMPILE. Entonces, tal como dijiste, en realidad no se trataba de detectar parámetros.Las banderas de seguimiento 2389 y 2390 tampoco ayudaron. Solo actualizar las estadísticas (
EXEC sp_updatestats
) lo hizo por mí.fuente