sql >> Base de Datos >  >> RDS >> Sqlserver

¿La asignación de parámetros de entrada de procedimientos almacenados a variables locales ayuda a optimizar la consulta?

No intentaré explicar los detalles completos de la detección de parámetros, pero en resumen, no, no siempre ayuda (y puede entorpecer).

Imagine una tabla (T) con una clave principal y una columna de Fecha indexada (A), en la tabla hay 1000 filas, 400 tienen el mismo valor de A (digamos hoy 20130122), las 600 filas restantes son los próximos 600 días , por lo que solo 1 registro por fecha.

Esta consulta:

SELECT *
FROM T
WHERE A = '20130122';

Producirá un plan de ejecución diferente a:

SELECT *
FROM T
WHERE A = '20130123';

Dado que las estadísticas indicarán que se devolverán las primeras 400 de 1000 filas, el optimizador debe reconocer que una exploración de tabla será más eficiente que una búsqueda de marcador, mientras que la segunda solo generará 1 fila, por lo que una búsqueda de marcador será mucho más eficiente. más eficiente.

Ahora, volviendo a su pregunta, si hiciéramos de esto un procedimiento:

CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
    SELECT *
    FROM T
    WHERE A = @Param

Entonces corre

EXECUTE dbo.GetFromT '20130122'; --400 rows

Se usará el plan de consulta con el escaneo de la tabla, si la primera vez que lo ejecuta usa '20130123' como parámetro, almacenará el plan de búsqueda de marcadores. Hasta que se vuelva a compilar el procedimiento, el plan seguirá siendo el mismo. Haciendo algo como esto:

CREATE PROCEDURE dbo.GetFromT @Param VARCHAR(5)
AS
    DECLARE @Param2 VARCHAR(5) = @Param;
    SELECT *
    FROM T
    WHERE A = @Param2

Entonces esto se ejecuta:

EXECUTE dbo.GetFromT '20130122';

Si bien el procedimiento se compila de una sola vez, no fluye correctamente, por lo que el plan de consulta creado en la primera compilación no tiene idea de que @Param2 será igual a @param, por lo que el optimizador (sin saber cuántas filas esperar) asumirá que se devolverán 300 (30%), por lo que considerará que un escaneo de tabla es más eficiente que una búsqueda de marcador. Si ejecutó el mismo procedimiento con '20130123' como parámetro, generaría el mismo plan (independientemente del parámetro con el que se invocó por primera vez) porque las estadísticas no se pueden usar para un valor desconocido. Por lo tanto, ejecutar este procedimiento para '20130122' sería más eficiente, pero para todos los demás valores sería menos eficiente que sin parámetros locales (suponiendo que el procedimiento sin parámetros locales se invocó primero con algo que no sea '20130122')

Algunas consultas para demostrar para que pueda ver los planes de ejecución por sí mismo

Crear esquema y datos de muestra

CREATE TABLE T (ID INT IDENTITY(1, 1) PRIMARY KEY, A DATE NOT NULL, B INT,C INT, D INT, E INT);

CREATE NONCLUSTERED INDEX IX_T ON T (A);

INSERT T (A, B, C, D, E)
SELECT  TOP 400 CAST('20130122' AS DATE), number, 2, 3, 4 
FROM    Master..spt_values 
WHERE   type = 'P'
UNION ALL
SELECT TOP 600 DATEADD(DAY, number, CAST('20130122' AS DATE)), number, 2, 3, 4 
FROM    Master..spt_values 
WHERE   Type = 'P';
GO
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
    SELECT *
    FROM T
    WHERE A = @Param
GO
CREATE PROCEDURE dbo.GetFromT2 @Param DATE
AS
    DECLARE @Param2 DATE = @Param;
    SELECT *
    FROM T
    WHERE A = @Param2
GO

Ejecutar procedimientos (mostrando el plan de ejecución real):

EXECUTE GetFromT '20130122';
EXECUTE GetFromT '20130123';
EXECUTE GetFromT2 '20130122';
EXECUTE GetFromT2 '20130123';
GO
EXECUTE SP_RECOMPILE GetFromT;
EXECUTE SP_RECOMPILE GetFromT2;
GO
EXECUTE GetFromT '20130123';
EXECUTE GetFromT '20130122';
EXECUTE GetFromT2 '20130123';
EXECUTE GetFromT2 '20130122';

Verás que la primera vez GetFromT se compila, usa un escaneo de tabla y lo retiene cuando se ejecuta con el parámetro '20130122', GetFromT2 también usa un escaneo de tabla y retiene el plan para '20130122'.

Después de que los procedimientos se hayan configurado para la recompilación y se vuelvan a ejecutar (observe en un orden diferente), GetFromT usa un bucle de marcador y retiene el plan para '20130122', a pesar de haber considerado previamente que un escaneo de tabla es un plan más apropiado. GetFromT2 no se ve afectado por la orden y tiene el mismo plan que antes de la recompilación.

Entonces, en resumen, depende de la distribución de sus datos y sus índices, su frecuencia de recompilación y un poco de suerte si un procedimiento se beneficiará del uso de variables locales. Ciertamente no siempre ayuda.

Espero haber arrojado algo de luz sobre el efecto del uso de parámetros locales, planes de ejecución y compilación de procedimientos almacenados. Si he fallado por completo o me he perdido un punto clave, puede encontrar una explicación mucho más detallada aquí:

http://www.sommarskog.se/query-plan-mysteries.html