В 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

В идеале я хотел бы посмотреть, какие параметры были для запущенной процедуры, которые заставляют ее работать так долго для определенного набора ошибочных параметров.

Есть способ сделать это? (В этом вопросе Аарон Бертран упоминает DBCC InputBuffer , но я не думаю, что это подходит для этой проблемы.)

user420667
источник
Действительно, единственный способ получить входные параметры или посмотреть, что было передано во время выполнения, - записать значения и вызов файла журнала. Это можно легко сделать с помощью RAISEERROR, если вы хотите увидеть это в журнале ошибок или, приложив немного больше усилий, записать его во внешний файл.
Стив Мангиамели

Ответы:

16

Эта информация - значения параметров времени выполнения, передаваемые в хранимую процедуру (т. Е. Вызов RPC) или параметризованный запрос - доступна только через трассировку SQL (и я предполагаю эквивалентное расширенное событие в более новых версиях SQL Server). Вы можете увидеть это, запустив SQL Server Profiler (поставляется с SQL Server) и выбора различных «Completed» события, такие как: RPC:Completed, SP:Completed, и SQL:BatchCompleted. Вам также нужно выбрать поле «TextData», так как значения будут там.

Разница между моим ответом и @ Kin в ответ на этот вопрос заключается в том , что @ ответ Kin в (если я не ошибаюсь, в этом случае я удалю это) фокусируется на том , как:

  • ваш собственный план запроса (в этом случае он может содержать информацию о параметрах времени выполнения, но не для других сеансов / SPID), или
  • планы из DMV (в этом случае они должны иметь только скомпилированные значения параметров, которые не являются значениями времени выполнения).

Мой ответ посвящен получению значений параметров для других сеансов, которые в данный момент выполняются. Опираясь на DMV, невозможно узнать, совпадает ли значение параметра среды выполнения со значением скомпилированного параметра. И контекст этого вопроса отслеживает значение времени выполнения запросов, отправляемых через другие Sessions / SPID (и в SQL Server 2005, тогда как расширенные события были введены в SQL Server 2008).

Соломон Руцкий
источник
13

Вы можете включить фактический план выполнения, а затем посмотреть на XML плана выполнения.

введите описание изображения здесь

Или вы можете использовать инструмент плана исследователь SQL часового и увидеть parametersвкладку, перечислят compiled valueи run 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 Trace является единственным способом ( без редактирования Sproc ).

Редактировать свой Sproc:

Однако следующая лучшая вещь - это немного отредактировать рассматриваемый Sproc.
Объявите переменную DateTime в начале текущего времени.
В конце Sproc запишите значения Sproc_StartTime, Sproc_EndTime и Parameter в таблицу.

Вы могли бы даже добавить некоторую условную логику, чтобы использовать DateDiff () только для регистрации, когда при обработке Sproc использовалось расширенное количество времени.
Это может ускорить работу вашего Sproc и уменьшить потребление пространства в вашем журнальном столе, когда Sproc работает наверху.

Затем у вас есть файл журнала, который вы можете запрашивать и анализировать в течение нескольких месяцев (без трассировки в Prod).
Когда вы закончите настройку Sproc, просто удалите несколько строк логики Timer и Logger, которые вы добавили.

Значения параметров кэшированного плана:

Следует отметить, что включение значений параметров текущего кэшированного плана в таблицу журналов может помочь вам определить, не усугубляют ли они проблему производительности .
Я использую, OPTIMIZE FORчтобы установить, как обрабатывать параметры в моем Sproc, когда я знаю, что он будет использоваться для разрезания и нарезания данных.
Я считаю, что использование OPTIMIZE FORдает последовательные и быстрые результаты при использовании того же Sproc с параметрами, что и дополнительные фильтры .
Это определенно меньше переменной, чтобы рассмотреть, если вы укажите, как обрабатывать их.

Ниже приведен пример того, что вы можете добавить в нижней части вашего оператора выбора:

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
источник
0

Я заметил, что при использовании запроса Erland Sommarskog для уничтожения XML-плана и извлечения ParameterCompiledValue первый CTE «basedata» не учитывает планы, которые имеют WARNINGS (например, неявные преобразования), поскольку 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
SQLcyclopedia
источник
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
Код только ответы не рекомендуется. Попробуйте добавить объяснение того, почему этот код решает проблему. Смотрите, как ответить
Питер