SQL Server 2012 et 2016 Standard: si je mets une logique if-else dans une procédure stockée pour exécuter l'une des deux branches de code, selon la valeur d'un paramètre, le moteur met-il en cache la dernière version?
Non, il met en cache toutes les versions. Ou plutôt, il met en cache une version avec tous les chemins explorés, compilés avec des variables passées en.
Voici une démonstration rapide, en utilisant la base de données Stack Overflow.
Créez un index:
CREATE INDEX ix_yourmom ON dbo.Users (Reputation) INCLUDE (Id, DisplayName);
GO
Créez une procédure stockée avec une indication d'index qui pointe vers un index qui n'existe pas, en code ramifié.
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 j'exécute ce proc stocké à la recherche de Réputation = 1, j'obtiens une erreur.
EXEC dbo.YourMom @Reputation = 1;
Msg 308, niveau 16, état 1, procédure YourMom, ligne 14 [ligne de démarrage par lots 32] Index 'ix_yourdad' sur la table 'dbo.Users' (spécifié dans la clause FROM) n'existe pas.
Si nous corrigeons le nom d'index et réexécutons la requête, le plan mis en cache ressemble à ceci:
A l'intérieur, le XML aura deux références à la @Reputation
variable.
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />
Un test un peu plus simple serait d'obtenir simplement un plan estimé pour le proc stocké. Vous pouvez voir l'optimiseur explorer les deux chemins:
Et si lors de l'exécution suivante, la valeur du paramètre change, va-t-il recompiler et remettre en cache la procédure stockée, car une branche différente du code doit être exécutée? (Cette requête est assez coûteuse à compiler.) Merci.
Non, il conservera la valeur d'exécution de la première compilation.
Si nous réexécutons avec un autre @Reputation
:
EXEC dbo.YourMom @Reputation = 2;
Du plan réel :
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />
Nous avons toujours une valeur compilée de 1, mais maintenant une valeur d'exécution de 2.
Dans le cache du plan, que vous pouvez consulter avec un outil gratuit comme celui développé par ma société, sp_BlitzCache :
La procédure stockée a été appelée deux fois et chaque instruction qu'elle contient a été appelée une fois.
Alors qu'est-ce que nous avons? Un plan mis en cache pour les deux requêtes dans la procédure stockée.
Si vous voulez ce type de logique ramifiée, vous devez appeler des procédures sous-stockées:
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;
Ou SQL dynamique:
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;
J'espère que cela t'aides!