SQL Server 2012 y 2016 Standard: si pongo lógica if-else en un procedimiento almacenado para ejecutar una de dos ramas de código, dependiendo del valor de un parámetro, ¿el motor almacena en caché la última versión?
No, almacena en caché todas las versiones. O más bien, almacena en caché una versión con todas las rutas exploradas, compiladas con variables pasadas.
Aquí hay una demostración rápida, utilizando la base de datos Stack Overflow.
Crea un índice:
CREATE INDEX ix_yourmom ON dbo.Users (Reputation) INCLUDE (Id, DisplayName);
GO
Cree un procedimiento almacenado con una pista de índice que apunte a un índice que no existe, en código ramificado.
CREATE OR ALTER PROCEDURE dbo.YourMom (@Reputation INT)
AS
BEGIN
IF @Reputation = 1
BEGIN
SELECT u.Id, u.DisplayName, u.Reputation
FROM dbo.Users AS u WITH (INDEX = PK_Users_Id)
WHERE u.Reputation = @Reputation;
END;
IF @Reputation > 1
BEGIN
SELECT u.Id, u.DisplayName, u.Reputation
FROM dbo.Users AS u WITH (INDEX = ix_yourdad)
WHERE u.Reputation = @Reputation;
END;
END;
Si ejecuto ese proceso almacenado buscando Reputación = 1, obtengo un error.
EXEC dbo.YourMom @Reputation = 1;
Msg 308, Nivel 16, Estado 1, Procedimiento YourMom, Línea 14 [Batch Start Line 32] Indice 'ix_yourdad' en la tabla 'dbo.Users' (especificado en la cláusula FROM) no existe.
Si arreglamos el nombre del índice y volvemos a ejecutar la consulta, el plan en caché se ve así:
En el interior, el XML tendrá dos referencias a la @Reputation
variable.
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />
Una prueba un poco más simple sería obtener un plan estimado para el proceso almacenado. Puede ver el optimizador explorando ambas rutas:
Y si en la siguiente ejecución, el valor del parámetro cambia, ¿volverá a compilar y volver a almacenar en caché el procedimiento almacenado, porque se debe ejecutar una rama diferente del código? (Esta consulta es bastante costosa de compilar). Gracias.
No, retendrá el valor de tiempo de ejecución de la primera compilación.
Si volvemos a ejecutar con un diferente @Reputation
:
EXEC dbo.YourMom @Reputation = 2;
Del plan actual :
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />
Todavía tenemos un valor compilado de 1, pero ahora un valor de tiempo de ejecución de 2.
En el caché del plan, que puede consultar con una herramienta gratuita como la que desarrolla mi empresa, sp_BlitzCache :
El procedimiento almacenado se ha llamado dos veces, y cada instrucción en él se ha llamado una vez.
¿Entonces que tenemos? Un plan en caché para ambas consultas en el procedimiento almacenado.
Si desea este tipo de lógica ramificada, debería llamar a procedimientos subalmacenados:
CREATE OR ALTER PROCEDURE dbo.YourMom (@Reputation INT)
AS
BEGIN
IF @Reputation = 1
BEGIN
EXEC dbo.Reputation1Query;
END;
IF @Reputation > 1
BEGIN
EXEC dbo.ReputationGreaterThan1Query;
END;
END;
O SQL dinámico:
DECLARE @sql NVARCHAR(MAX) = N''
SET @sql +=
N'
SELECT u.Id, u.DisplayName, u.Reputation
FROM dbo.Users AS u '
IF @Reputation = 1
BEGIN
SET @sql += N' (INDEX = PK_Users_Id)
WHERE u.Reputation = @Reputation;'
END;
IF @Reputation > 1
BEGIN
SET @sql += ' WITH (INDEX = ix_yourmom)
WHERE u.Reputation = @Reputation;'
END;
EXEC sys.sp_executesql @sql;
¡Espero que esto ayude!