In SQL Server esiste un modo per determinare i valori dei parametri passati a una procedura memorizzata in esecuzione


13

Un modo per determinare l'esecuzione della stored procedure è utilizzare i metodi di "gestione dinamica", in questo modo:

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

Tuttavia, questo visualizza solo il testo dell'istruzione create della procedura memorizzata. per esempio:

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

Idealmente, vorrei vedere quali erano i parametri per la procedura in esecuzione che stanno causando l'esecuzione così a lungo per il particolare set di parametri offensivi.

C'è un modo per farlo? (In questa domanda Aaron Bertrand menziona DBCC InputBuffer , ma non credo sia appropriato per questo problema.)


Davvero l'unico modo per acquisire i parametri di input o vedere cosa è stato passato in fase di esecuzione è registrare i valori e la chiamata in un file di registro. Puoi farlo facilmente con RAISEERROR se vuoi vederlo nel registro degli errori o con un po 'più di sforzo, scriverlo su un file esterno da qualche parte.
Steve Mangiameli,

Risposte:


16

Queste informazioni - valori dei parametri di runtime passati in una Stored Procedure (ovvero chiamata RPC) o query parametrizzata - sono disponibili solo tramite una traccia SQL (e presumo che l'Evento esteso equivalente nelle nuove versioni di SQL Server). Si può vedere questo eseguendo SQL Server Profiler (si tratta con SQL Server) e selezionando i vari eventi "completato", come ad esempio: RPC:Completed, SP:Completed, eSQL:BatchCompleted . È inoltre necessario selezionare il campo "TextData" poiché i valori saranno presenti.

La differenza tra la mia risposta e di @ Kin risposta su questa domanda è che la risposta di @ Kin (se non erro, nel qual caso mi tolgo questo) si concentra su come ottenere uno:

  • il proprio piano di query (nel qual caso può contenere al suo interno le informazioni sui parametri di runtime, ma non per altre Sessioni / SPID), oppure
  • piani dai DMV (nel qual caso dovrebbero avere solo i valori dei parametri compilati, che non sono valori di runtime).

La mia risposta si concentra sull'ottenere i valori dei parametri per altre sessioni attualmente in esecuzione. Quando si fa affidamento sui DMV, non è possibile sapere se il valore del parametro di runtime è uguale al valore del parametro compilato. E il contesto di questa domanda sta rintracciando il valore di runtime delle query inviate tramite altre Sessioni / SPID (e in SQL Server 2005, mentre gli eventi estesi sono stati introdotti in SQL Server 2008).


13

È possibile attivare il piano di esecuzione effettivo e quindi guardare l'XML del piano di esecuzione.

inserisci qui la descrizione dell'immagine

Oppure puoi usare lo strumento di esplorazione del piano di sql sentry e vedere la parametersscheda che elencherà il compiled valuee run time valueper il piano di esecuzione effettivo.

Se non è possibile attivare il piano effettivo, è possibile esaminare la cache del piano come descritto di seguito.

-- 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

5
La cache del piano ha solo i valori compilati anziché i valori per una specifica esecuzione successiva. Potrebbe anche utilizzare l' Showplan XML Statistics Profileevento in Profiler per ottenere il piano effettivo, anche se se si distribuisce Profiler ci sarebbero modi meno intensi per ottenerlo.
Martin Smith,

1

@SolomonRutzky ha ragione.
Traccia SQL Profiler è l'unico modo ( senza modificare Sproc ).

Modifica il tuo Sproc:

Tuttavia , la cosa migliore da fare è modificare leggermente lo Sproc in questione.
Dichiarare una variabile DateTime all'inizio con l'ora corrente.
Alla fine dello Sproc, registra i valori Sproc_StartTime, Sproc_EndTime e Parameter in una tabella.

È anche possibile aggiungere un po 'di logica condizionale per utilizzare un DateDiff () per la registrazione solo quando è stato utilizzato un periodo di tempo prolungato nell'elaborazione di Sproc.
Ciò può accelerare Sproc e ridurre il consumo di spazio della tabella di registro per quando Sproc è in esecuzione in punta di vertice.

Quindi hai un file di registro che puoi interrogare e analizzare per mesi (senza una traccia in esecuzione in Prod).
Al termine dell'ottimizzazione di Sproc, è sufficiente eliminare le poche righe della logica Timer e Logger aggiunte.

Valori dei parametri del piano cache:

Devo dire che l'inclusione dei valori dei parametri del piano cache corrente nella tabella dei registri può aiutare a determinare se stanno aggravando il problema delle prestazioni .
Uso OPTIMIZE FORper impostare come gestire i parametri nel mio Sproc quando so che verrà utilizzato per tagliare e tagliare i dati.
Trovo che l'utilizzo di OPTIMIZE FORrisultati coerenti e rapidi quando si utilizza lo stesso Sproc con parametri come filtri opzionali .
È sicuramente una variabile in meno da considerare se si specifica come gestirle.

Di seguito è riportato un esempio di ciò che è possibile aggiungere in fondo all'istruzione 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))

0

Ho notato quando si utilizzava la query di Erland Sommarskog per distruggere l'XML del piano ed estrarre ParameterCompiledValue che il primo CTE "basedata" non tiene conto dei piani che hanno AVVERTENZE (ad esempio conversioni implicite) poiché CHARINDEX (funzione integrata) cerca la stringa di corrispondenza della prima espressione input (ie) e tali avvertimenti usano queste stesse frasi / nodi.

Propongo quindi di sostituire questa sezione con la sezione rivista di seguito:

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

Sezione rivista:

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

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 

2
Le risposte solo al codice sono scoraggiate. Valuta di aggiungere una spiegazione del perché questo codice risolve un problema. Vedi come rispondere
Peter Vandivier,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.