En SQL Server, ¿hay alguna manera de determinar los valores de los parámetros pasados ​​a un procedimiento almacenado en ejecución?

13

Una forma de determinar el procedimiento almacenado en ejecución es utilizar métodos de "administración dinámica", de esta manera:

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

Sin embargo, esto solo muestra el texto de la instrucción create del procedimiento almacenado. p.ej:

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

Idealmente, me gustaría ver cuáles fueron los parámetros para el procedimiento de ejecución que hacen que se ejecute tanto tiempo para el conjunto particular de parámetros ofensivos.

¿Hay una manera de hacer eso? (En esta pregunta, Aaron Bertrand menciona DBCC InputBuffer , pero no creo que sea apropiado para este problema).

usuario420667
fuente
Realmente, la única forma de capturar los parámetros de entrada o ver lo que se pasó en tiempo de ejecución es registrar los valores y la llamada a un archivo de registro. Puede hacer esto fácilmente con RAISEERROR si desea verlo en el registro de errores o, con un poco más de esfuerzo, escríbalo en un archivo externo en alguna parte.
Steve Mangiameli

Respuestas:

16

Esta información (valores de parámetros en tiempo de ejecución pasados ​​a un procedimiento almacenado (es decir, una llamada RPC) o una consulta parametrizada) solo está disponible a través de un seguimiento de SQL (y supongo que el evento extendido equivalente en las versiones más nuevas de SQL Server). Esto se puede ver mediante la ejecución de Analizador de SQL Server (que viene con SQL Server) y la selección de los distintos eventos "completadas", tales como: RPC:Completed, SP:Completed, y SQL:BatchCompleted. También debe seleccionar el campo "TextData" ya que los valores estarán allí.

La diferencia entre mi respuesta y @ de Kin respuesta a esta cuestión es que la respuesta de @ Kin (si no me equivoco, en cuyo caso voy a eliminar esta) se centra en conseguir ya sea:

  • su propio plan de consulta (en cuyo caso puede contener la información del parámetro de tiempo de ejecución, pero no para otras Sesiones / SPID), o
  • planes de los DMV (en cuyo caso solo deberían tener los valores de parámetros compilados, que no son valores de tiempo de ejecución).

Mi respuesta se centra en obtener los valores de los parámetros para otras sesiones que se están ejecutando actualmente. Al confiar en los DMV, no hay forma de saber si el valor del parámetro de tiempo de ejecución es el mismo que el valor del parámetro compilado. Y el contexto de esta pregunta es rastrear el valor de tiempo de ejecución de las consultas que se envían a través de otras Sesiones / SPID (y en SQL Server 2005, mientras que los Eventos extendidos se introdujeron en SQL Server 2008).

Solomon Rutzky
fuente
13

Usted puede activar el plan de ejecución real y luego mirar el plan de ejecución XML.

ingrese la descripción de la imagen aquí

O puede utilizar la herramienta del explorador plan de centinela SQL y ver la parameterspestaña que enumerará el compiled valuey run time valuepara el plan de ejecución real.

Si no puede activar el plan real, puede buscar en la memoria caché del plan como se describe a continuación.

-- 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
fuente
55
El caché del plan solo tiene los valores compilados en lugar de los valores para una ejecución específica posterior. También podría usar el Showplan XML Statistics Profileevento en Profiler para obtener el plan real, aunque si elimina Profiler habría formas menos intensivas de obtener esto.
Martin Smith
1

@SolomonRutzky tiene razón.
SQL Profiler Trace es la única forma ( sin editar el Sproc ).

Edite su Sproc:

Sin embargo , la siguiente mejor opción es editar ligeramente el Sproc en cuestión.
Declare una variable DateTime al principio con la hora actual.
Al final de Sproc, registre Sproc_StartTime, Sproc_EndTime y los valores de los parámetros en una tabla.

Incluso podría agregar algo de lógica condicional para usar un DateDiff () solo para el registro cuando se utilizó una cantidad de tiempo prolongada en el procesamiento de Sproc.
Esto puede acelerar su Sproc y reducir el consumo de espacio de su tabla de registro para cuando el Sproc se ejecuta de la mejor manera.

Luego tiene un archivo de registro que puede consultar y analizar durante meses (sin un seguimiento ejecutándose en Prod).
Cuando haya terminado de ajustar su Sproc, simplemente elimine las pocas líneas de lógica de temporizador y registrador que agregó.

Valores de parámetros del plan en caché:

Debo mencionar que incluir los valores actuales de los parámetros del plan en caché en su tabla de registro puede ayudarlo a determinar si están agravando el problema de rendimiento .
Utilizo OPTIMIZE FORpara configurar cómo manejar los parámetros en mi Sproc cuando sé que se usará para cortar y cortar datos.
Creo que el uso OPTIMIZE FORproduce resultados consistentes y rápidos cuando se usa el mismo Sproc con parámetros que los filtros opcionales .
Definitivamente es una variable menos a tener en cuenta si especifica cómo manejarlos.

A continuación se muestra un ejemplo de lo que puede agregar al final de su Select-Statement:

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

Me di cuenta al usar la consulta de Erland Sommarskog para destruir el plan XML y extraer ParameterCompiledValue que el primer CTE "basedata" no tiene en cuenta los planes que tienen ADVERTENCIAS (por ejemplo, conversiones implícitas) ya que el CHARINDEX (función incorporada) busca la primera cadena de coincidencia de expresión input (es decir) y tales advertencias usan estas mismas frases / nodos.

Por lo tanto, propongo reemplazar esta sección con la sección revisada a continuación:

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

Sección revisada:

       CHARINDEX('<ParameterList><ColumnReference', qp.query_plan) + LEN('<ParameterList>') AS paramstart,
       CHARINDEX('</ParameterList></QueryPlan>', qp.query_plan) AS paramend
Enciclopedia SQL
fuente
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
fuente
2
Las respuestas de solo código se desaconsejan. Considere agregar una explicación de por qué este código resuelve un problema. Vea cómo responder
Peter Vandivier