Plan d'exécution différent lors de l'exécution directe d'une instruction et à partir d'une procédure stockée
-
05-07-2019 - |
Question
Lors du développement d'une nouvelle requête au travail, je l'ai écrite et profilée dans l'Analyseur de requêtes SQL. La requête fonctionnait vraiment bien sans analyse de table, mais lorsque je l'ai encapsulée dans une procédure stockée, les performances étaient horribles. Lorsque j'ai examiné le plan d'exécution, j'ai constaté que SQL Server avait choisi un plan différent qui utilisait une analyse de table au lieu d'une recherche d'index sur TableB (j'ai été forcé de masquer un peu les noms de table et de colonne, mais aucune partie de la logique de requête. a changé).
Voici la requête
SELECT
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)) AS Day,
DATEPART(hh, TableA.Created) AS [Hour],
SUM(TableB.Quantity) AS Quantity,
SUM(TableB.Amount) AS Amount
FROM
TableA
INNER JOIN TableB ON TableA.BID = TableB.ID
WHERE
(TableA.ShopId = @ShopId)
GROUP BY
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)),
DATEPART(hh, TableA.Created)
ORDER BY
DATEPART(hh, TableA.Created)
Lorsque j'exécute la requête " raw " Je reçois les statistiques de suivi suivantes
Event Class Duration CPU Reads Writes SQL:StmtCompleted 75 41 7 0
Et quand j'exécute la requête en tant que proc stocké en utilisant la commande suivante
DECLARE @ShopId int
SELECT @ShopId = 1
EXEC spStats_GetSalesStatsByHour @ShopId
Je reçois les statistiques de trace suivantes
Event Class Duration CPU Reads Writes SQL:StmtCompleted 222 10 48 0
J'obtiens également le même résultat si je stocke la requête dans un nvarchar et l'exécute à l'aide de sp_executesql comme ceci (il fonctionne comme le sproc)
DECLARE @SQL nvarchar(2000)
SET @SQL = 'SELECT DATEADD(dd, ...'
exec sp_executesql @SQL
La procédure stockée ne contient rien à l'exception de l'instruction select ci-dessus. Qu'est-ce qui ferait que SQL Server choisisse un plan d'exécution inférieur simplement parce que l'instruction est exécutée en tant que procédure stockée?
Nous utilisons actuellement SQL Server 2000
.La solution
Cela a généralement quelque chose à voir avec le sniffing de paramètres. Cela peut être très frustrant. Parfois, il peut être résolu en recompilant la procédure stockée, et parfois vous pouvez même utiliser une variable en double dans la procédure stockée comme ceci:
alter procedure p_myproc (@p1 int) as
declare @p1_copy int;
set @p1_copy = @p1;
Ensuite, utilisez @ p1_copy dans la requête. Cela semble ridicule, mais cela fonctionne.
Consultez ma question récente sur le même sujet:
Autres conseils
Oui, je l'avais aussi vu sur Oracle DB 11g. La même requête fonctionnait rapidement sur 2 nœuds du serveur de base de données à l'invite de SQL MAIS lorsqu'elle était appelée depuis un paquet, elle se bloquait littéralement!
a dû effacer le pool partagé pour obtenir un comportement identique: raison de l'exécution d'un travail / script dont l'ancienne copie était verrouillée dans la mémoire cache / mémoire de la bibliothèque sur un nœud avec un plan d'exécution inférieur.