在SQL Server中,有一种方法可以确定传递给正在执行的存储过程的参数的值


13

确定执行存储过程的一种方法是使用“动态管理”方法,如下所示:

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

但是,这仅显示存储过程的create语句的文本。例如:

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

理想情况下,我想查看正在运行的过程的参数是什么,从而导致该过程对于特定的有问题的参数集运行了这么长时间。

有没有办法做到这一点?(在此问题中, Aaron Bertrand提到了DBCC InputBuffer,但我认为这不适用于此问题。)


捕获输入参数或查看在运行时传入的内容的唯一真正方法是将值和调用记录到日志文件中。如果您想在错误日志中查看它,则可以使用RAISEERROR轻松地执行此操作,或者稍加努力,将其写到某个位置的外部文件中。
Steve Mangiameli

Answers:


16

此信息-传递到存储过程(即RPC调用)或参数化查询中的运行时参数值-仅可通过SQL跟踪获得(并且我认为在SQL Server的较新版本中,等效的扩展事件)。您可以通过运行SQL Server事件探查器(它与SQL服务器),并选择不同的“已完成”的事件,比如看到:RPC:CompletedSP:Completed,和SQL:BatchCompleted。您还需要选择“ TextData”字段,因为其中将包含值。

我的答案与@Kin 对这个问题的答案之间的区别在于,@ Kin的答案(除非我弄错了,在这种情况下,我将删除此答案)着重于以下任一方面:

  • 您自己的查询计划(在这种情况下,它可以包含运行时参数信息,但不适用于其他会话/ SPID),或者
  • DMV中的计划(在这种情况下,它们仅具有已编译的参数值,而不是运行时值)。

我的答案集中在获取当前正在运行的其他会话的参数值。当依赖DMV时,无法知道运行时参数值是否与编译后的参数值相同。这个问题的上下文是跟踪通过其他会话/ SPID(以及在SQL Server 2005中提交的查询的运行时值),而在SQL Server 2008中引入了扩展事件。


13

可以打开实际的执行计划,然后查看执行计划XML。

在此处输入图片说明

或者,您可以使用sql sentry的计划资源管理器工具,并查看parameters列出了compiled valuerun time value的实际执行计划的标签。

如果您无法打开实际计划,则可以按照以下说明查看计划缓存。

-- 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
计划缓存仅具有编译后的值,而没有特定运行以后的值。也可以Showplan XML Statistics Profile在Profiler中使用事件来获取实际计划,但是如果将Profiler推出,则可以获得的强度较低的方法。
马丁·史密斯

1

@SolomonRutzky是正确的。
SQL Profiler跟踪是唯一的方法(无需编辑Sproc)。

编辑您的存储过程:

但是,下一个最好的方法是稍微修改所讨论的Sproc。
在当前时间的开头声明一个DateTime变量。
在Sproc的末尾,将Sproc_StartTime,Sproc_EndTime和参数值记录到表中。

您甚至可以添加一些条件逻辑来使用DateDiff()仅在处理Sproc时使用了较长的时间时才记录日志。
当Sproc运行于顶部时,这可以加快Sproc的速度并减少日志表的空间消耗。

然后,您将获得一个日志文件,可以查询和分析数月(没有在Prod中运行跟踪)。
完成对Sproc的调整后,只需删除添加的Timer和Logger逻辑的几行即可。

缓存计划参数值:

我应该提到,在日志表中包含“当前缓存的计划参数值”可能会帮助您确定它们是否使性能问题更为复杂
我使用OPTIMIZE FOR来设定如何处理的参数在我的存储过程时,我知道这将被用于切片和切块数据。
我发现OPTIMIZE FOR当使用带参数的Sproc和Optional Filters时,使用会产生一致且快速的结果。
如果指定如何处理它们,绝对是要考虑的变量少一点。

以下是您可以添加到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))

0

我注意到在使用Erland Sommarskog的查询切细计划XML并提取ParameterCompiledValue时,第一个“ basedata” CTE不考虑具有警告(例如隐式转换)的计划,因为CHARINDEX(内置函数)正在寻找与表达式匹配的第一个字符串输入(即),并且此类警告使用相同的短语/节点。

因此,我建议将本节替换为以下修订版:

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

修订部分:

       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.
马特

-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
不鼓励仅使用代码的答案。考虑添加解释此代码为何解决问题的原因。请参阅如何回答
彼得Vandivier
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.