Dans SQL Server, existe-t-il un moyen de déterminer les valeurs des paramètres passés à une procédure stockée en cours d'exécution

13

Une façon de déterminer la procédure stockée en cours d'exécution est d'utiliser des méthodes de "gestion dynamique", comme ceci:

SELECT 
    sqlText.Text, req.* 
FROM 
    sys.dm_exec_requests req
OUTER APPLY 
    sys.dm_exec_sql_text(req.sql_handle) AS sqltext

Toutefois, cela affiche uniquement le texte de l'instruction create de la procédure stockée. par exemple:

CREATE PROCEDURE IMaProcedure @id int AS SELECT * FROM AllTheThings Where id = @id

Idéalement, j'aimerais voir quels étaient les paramètres de la procédure en cours qui la font fonctionner si longtemps pour l'ensemble particulier de paramètres incriminés.

Y-a-t-il un moyen de faire ça? (Dans cette question, Aaron Bertrand mentionne DBCC InputBuffer , mais je ne pense pas que ce soit approprié pour ce problème.)

user420667
la source
Vraiment, la seule façon de capturer les paramètres d'entrée ou de voir ce qui a été transmis au moment de l'exécution est d'enregistrer les valeurs et l'appel dans un fichier journal. Vous pouvez le faire facilement avec RAISEERROR si vous voulez le voir dans le journal des erreurs ou avec un peu plus d'effort, écrivez-le quelque part dans un fichier externe.
Steve Mangiameli

Réponses:

16

Ces informations - les valeurs des paramètres d'exécution passées dans une procédure stockée (c'est-à-dire un appel RPC) ou une requête paramétrée - sont uniquement disponibles via une trace SQL (et je suppose que l'événement étendu équivalent dans les versions plus récentes de SQL Server). Vous pouvez voir cela en exécutant SQL Server Profiler (il est livré avec SQL Server) et en sélectionnant les différents événements « Terminé », tels que: RPC:Completed, SP:Completed, et SQL:BatchCompleted. Vous devez également sélectionner le champ "TextData" car les valeurs y seront.

La différence entre ma réponse et @ Kin réponse à cette question est que la réponse @ Kin ( à moins que je ne me trompe, dans ce cas , je vais supprimer cette) se concentre sur l' obtention soit:

  • votre propre plan de requête (auquel cas il peut contenir les informations des paramètres d'exécution, mais pas pour les autres sessions / SPID), ou
  • plans des DMV (auquel cas ils ne devraient avoir que les valeurs des paramètres compilés, qui ne sont pas des valeurs d'exécution).

Ma réponse se concentre sur l'obtention des valeurs des paramètres pour les autres sessions en cours d'exécution. Lorsque vous vous appuyez sur les DMV, il n'y a aucun moyen de savoir si la valeur du paramètre d'exécution est la même que la valeur du paramètre compilé. Et le contexte de cette question est de rechercher la valeur d'exécution des requêtes soumises via d'autres sessions / SPID (et dans SQL Server 2005, tandis que les événements étendus ont été introduits dans SQL Server 2008).

Solomon Rutzky
la source
13

Vous pouvez activer le plan d'exécution réel, puis consulter le XML du plan d'exécution.

entrez la description de l'image ici

Ou vous pouvez utiliser l'outil explorateur de plan de sql sentry et voir l' parametersonglet qui listera le compiled valueet run time valuele plan d'exécution réel.

Si vous ne pouvez pas activer le plan réel, vous pouvez consulter le cache du plan comme décrit ci-dessous.

-- borrowed from  Erland Sommarskog
-- Link : http://www.sommarskog.se/query-plan-mysteries.html#dmvgettingplans
-- Remember that you are looking at the estimated plan so the actual no. of rows and actual executions wont be there ! <-- Important why a particular plan is bad.

DECLARE @dbname    nvarchar(256),
        @procname  nvarchar(256)
SELECT @dbname = 'Northwind',  -- Your DB name
       @procname = 'dbo.List_orders_11' -- The SP that you want to get parameters for !

; WITH basedata AS (
   SELECT qs.statement_start_offset/2 AS stmt_start,
          qs.statement_end_offset/2 AS stmt_end,
          est.encrypted AS isencrypted, est.text AS sqltext,
          epa.value AS set_options, qp.query_plan,
          charindex('<ParameterList>', qp.query_plan) + len('<ParameterList>')
             AS paramstart,
          charindex('</ParameterList>', qp.query_plan) AS paramend
   FROM   sys.dm_exec_query_stats qs
   CROSS  APPLY sys.dm_exec_sql_text(qs.sql_handle) est
   CROSS  APPLY sys.dm_exec_text_query_plan(qs.plan_handle,
                                            qs.statement_start_offset,
                                            qs.statement_end_offset) qp
   CROSS  APPLY sys.dm_exec_plan_attributes(qs.plan_handle) epa
   WHERE  est.objectid  = object_id (@procname)
     AND  est.dbid      = db_id(@dbname)
     AND  epa.attribute = 'set_options'
), next_level AS (
   SELECT stmt_start, set_options, query_plan,
          CASE WHEN isencrypted = 1 THEN '-- ENCRYPTED'
               WHEN stmt_start >= 0
               THEN substring(sqltext, stmt_start + 1,
                              CASE stmt_end
                                   WHEN 0 THEN datalength(sqltext)
                                   ELSE stmt_end - stmt_start + 1
                              END)
          END AS Statement,
          CASE WHEN paramend > paramstart
               THEN CAST (substring(query_plan, paramstart,
                                   paramend - paramstart) AS xml)
          END AS params
   FROM   basedata
)
SELECT set_options AS [SET], n.stmt_start AS Pos, n.Statement,
       CR.c.value('@Column', 'nvarchar(128)') AS Parameter,
       CR.c.value('@ParameterCompiledValue', 'nvarchar(128)') AS [Sniffed Value],
       CAST (query_plan AS xml) AS [Query plan]
FROM   next_level n
CROSS  APPLY   n.params.nodes('ColumnReference') AS CR(c)
ORDER  BY n.set_options, n.stmt_start, Parameter
Kin Shah
la source
5
Le cache de plan n'a que les valeurs compilées plutôt que les valeurs pour une exécution spécifique ultérieure. Pourrait également utiliser l' Showplan XML Statistics Profileévénement dans Profiler pour obtenir le plan réel, mais si vous retirez Profiler, il y aurait des moyens moins intensifs d'obtenir cela.
Martin Smith
1

@SolomonRutzky a raison.
La trace SQL Profiler est le seul moyen ( sans modifier le Sproc ).

Modifiez votre Sproc:

Cependant , la meilleure chose à faire est de modifier légèrement le Sproc en question.
Déclarez une variable DateTime au début avec l'heure actuelle.
À la fin du Sproc, enregistrez les valeurs Sproc_StartTime, Sproc_EndTime et Parameter dans une table.

Vous pouvez même ajouter une logique conditionnelle pour utiliser un DateDiff () pour la journalisation uniquement lorsqu'une période de temps étendue a été utilisée dans le traitement du Sproc.
Cela peut accélérer votre Sproc et réduire la consommation d'espace de votre table de journal lorsque le Sproc est en cours d'exécution.

Ensuite, vous avez un fichier journal que vous pouvez interroger et analyser au cours des mois (sans trace en cours d'exécution dans Prod).
Lorsque vous avez terminé de régler votre Sproc, supprimez simplement les quelques lignes de logique Timer et Logger que vous avez ajoutées.

Valeurs des paramètres du plan mis en cache:

Je dois mentionner que l'inclusion des valeurs actuelles des paramètres du plan mis en cache dans votre table de journal peut vous aider à déterminer si elles aggravent le problème de performances .
J'utilise OPTIMIZE FORpour définir la façon de gérer les paramètres dans mon Sproc quand je sais qu'il sera utilisé pour le découpage et le découpage des données.
Je trouve que l'utilisation OPTIMIZE FORdonne des résultats cohérents et rapides lorsque vous utilisez le même Sproc avec des paramètres que des filtres optionnels .
C'est certainement une variable de moins à considérer si vous spécifiez comment les gérer.

Vous trouverez ci-dessous un exemple de ce que vous pourriez ajouter au bas de votre instruction Select:

OPTION(OPTIMIZE FOR (@SiteID = 'ABC',
                     @LocationID = NULL, @DepartmentID = NULL,
                     @EmployeeID = NULL, @CustomerID = NULL,
                     @ProductID = NULL, @OrderID = NULL, @OrderStatusID = NULL,
                     @IncludedCancelledOrders = 1,
                     @StartDate UNKNOWN, @EndDate UNKNOWN))
MikeTeeVee
la source
0

J'ai remarqué lors de l'utilisation de la requête d'Erland Sommarskog pour déchiqueter le plan XML et extraire ParameterCompiledValue que le premier CTE "basedata" ne tient pas compte des plans qui ont des AVERTISSEMENTS (par exemple des conversions implicites) car CHARINDEX (fonction intégrée) recherche la première chaîne de correspondance d'expression. (c.-à-d.) et ces avertissements utilisent ces mêmes phrases / nœuds.

Je propose donc de remplacer cette section par la section révisée ci-dessous:

      CHARINDEX('<ParameterList>', qp.query_plan) + LEN('<ParameterList>') AS paramstart,
      CHARINDEX('</ParameterList>', qp.query_plan) AS paramend

Section révisée:

       CHARINDEX('<ParameterList><ColumnReference', qp.query_plan) + LEN('<ParameterList>') AS paramstart,
       CHARINDEX('</ParameterList></QueryPlan>', qp.query_plan) AS paramend
SQLcyclopedia
la source
Disallowed implicit conversion from data type xml to data type varchar, table 'sys.dm_exec_query_plan', column 'query_plan'. Use the CONVERT function to run this query.
Matt
-1
SELECT DB_NAME(req.database_id),
sqltext.TEXT,
req.session_id,
req.status,
req.start_time,
req.command,
req.cpu_time,
req.total_elapsed_time ,   REPLACE(REPLACE(REPLACE(REPLACE(
CONVERT(VARCHAR(MAX), CONVERT(XML, REPLACE( query_plan, 'xmlns="','xmlns1="')).query('//        ParameterList/ColumnReference')),
'<ColumnReference Column="','declare '),
'" ParameterDataType="',' '),
'" ParameterCompiledValue="(',' = '),
')"/>', CONCAT(';', CHAR(10) , CHAR(13))) ParameterList
FROM sys.dm_exec_requests req
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext 
 CROSS  APPLY sys.dm_exec_text_query_plan(plan_handle, statement_start_offset, statement_end_offset) qp
order by req.total_elapsed_time desc 
Jatin Bhole
la source
2
Seules les réponses codées sont déconseillées. Pensez à ajouter une explication de la raison pour laquelle ce code résout un problème. Voir Comment répondre
Peter Vandivier