SQL Server 2012 e 2016 Standard: se inserisco la logica if-else in una procedura memorizzata per eseguire uno dei due rami del codice, a seconda del valore di un parametro, il motore memorizza nella cache l'ultima versione?
No, memorizza nella cache tutte le versioni. O meglio, memorizza nella cache una versione con tutti i percorsi esplorati, compilati con variabili passate.
Ecco una breve demo, usando il database Stack Overflow.
Crea un indice:
CREATE INDEX ix_yourmom ON dbo.Users (Reputation) INCLUDE (Id, DisplayName);
GO
Creare una procedura memorizzata con un suggerimento indice che punta a un indice che non esiste, nel codice ramificato.
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;
Se eseguo quel proc memorizzato cercando Reputation = 1, ottengo un errore.
EXEC dbo.YourMom @Reputation = 1;
Messaggio 308, livello 16, stato 1, procedura YourMom, riga 14 [Batch Start Line 32] L'indice "ix_yourdad" nella tabella "dbo.Users" (specificato nella clausola FROM) non esiste.
Se ripariamo il nome dell'indice ed eseguiamo nuovamente la query, il piano memorizzato nella cache è simile al seguente:
All'interno, l'XML avrà due riferimenti alla @Reputation
variabile.
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />
Un test leggermente più semplice sarebbe quello di ottenere solo un piano stimato per il proc memorizzato. Puoi vedere l'ottimizzatore che esplora entrambi i percorsi:
E se sulla seguente esecuzione, il valore del parametro cambia, ricompilerà e ricodificherà la procedura memorizzata, perché deve essere eseguito un ramo diverso del codice? (Questa query è piuttosto costosa da compilare.) Grazie.
No, manterrà il valore di runtime della prima compilation.
Se eseguiamo nuovamente l'esecuzione con un diverso @Reputation
:
EXEC dbo.YourMom @Reputation = 2;
Dal piano attuale :
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />
Abbiamo ancora un valore compilato di 1, ma ora un valore di runtime di 2.
Nella cache del piano, che puoi verificare con uno strumento gratuito come quello sviluppato dalla mia azienda, sp_BlitzCache :
La procedura memorizzata è stata chiamata due volte e ogni istruzione in essa è stata chiamata una volta.
Allora, cosa abbiamo? Un piano memorizzato nella cache per entrambe le query nella procedura memorizzata.
Se si desidera questo tipo di logica ramificata, è necessario chiamare le procedure memorizzate secondarie:
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 dinamico:
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;
Spero che sia di aiuto!