web-dev-qa-db-fra.com

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

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 d'exécution 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 questionAaron Bertrand mentionne DBCC InputBuffer , mais je ne pense pas que ce soit approprié pour ce problème.)

13
user420667

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 le voir en exécutant SQL Server Profiler (il est fourni avec SQL Server) et en sélectionnant les divers événements "Terminés", 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's réponse sur cette question est que la réponse de @ Kin (à moins que je ne me trompe, auquel cas je supprimerai cela) se concentre sur l'obtention soit:

  • le vôtre plan de requête (auquel cas il peut contenir les informations sur les paramètres d'exécution, mais pas pour les autres sessions/SPID), ou
  • les plans des DMV (auquel cas ils devraient n'ont 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 autre 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).

16
Solomon Rutzky

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

enter image description here

Ou vous pouvez utiliser outil Explorateur de plans de sentinelle sql et voir l'onglet parameters qui listera les compiled value et run time value pour le 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
13
Kin Shah

@SolomonRutzky a raison.
La trace du profileur SQL 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, connectez les valeurs Sproc_StartTime, Sproc_EndTime et Parameter à 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é le réglage de 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 FOR pour définir comment 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 qu'en utilisant OPTIMIZE FOR donne des résultats cohérents et rapides lors de l'utilisation du même Sproc avec des paramètres comme 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))
1
MikeTeeVee

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 AVERTISSEMENTS (par exemple implicite conversions) car CHARINDEX (fonction intégrée) recherche la première expression correspondant à l'entrée de chaîne (c'est-à-dire) 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
0
SQLcyclopedia