No SQL Server, existe uma maneira de determinar os valores dos parâmetros passados ​​para um procedimento armazenado em execução

13

Uma maneira de determinar o procedimento armazenado em execução é usar métodos de "gerenciamento dinâmico", como:

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

No entanto, isso exibe apenas o texto da instrução de criação do procedimento armazenado. por exemplo:

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

Idealmente, gostaria de ver quais eram os parâmetros para o procedimento em execução que estão causando a execução por tanto tempo para o conjunto específico de parâmetros incorretos.

Existe uma maneira de fazer isso? ( Nesta pergunta, Aaron Bertrand menciona o DBCC InputBuffer , mas não acho que seja apropriado para esse problema.)

user420667
fonte
Realmente, a única maneira de capturar os parâmetros de entrada ou ver o que foi passado no tempo de execução é registrar os valores e a chamada em um arquivo de log. Você pode fazer isso facilmente com o RAISEERROR se quiser vê-lo no log de erros ou, com um pouco mais de esforço, gravá-lo em um arquivo externo em algum lugar.
27516 Steve Swameli

Respostas:

16

Essas informações - valores de parâmetros em tempo de execução passados ​​para um Procedimento armazenado (chamada RPC) ou consulta parametrizada - estão disponíveis apenas através de um rastreamento SQL (e eu presumo que o evento estendido equivalente nas versões mais recentes do SQL Server). Você pode ver isso executando SQL Server Profiler (que vem com o SQL Server) e selecionando os vários eventos "concluído", tais como: RPC:Completed, SP:Completed, e SQL:BatchCompleted. Você também precisa selecionar o campo "TextData", pois os valores estarão lá.

A diferença entre a minha resposta e de @ Kin resposta a esta questão é que resposta da @ Kin (se não me engano, caso em que eu vou remover esse) se concentra em obter ou:

  • seu próprio plano de consulta (nesse caso, ele pode conter as informações do parâmetro de tempo de execução, mas não para outras sessões / SPIDs) ou
  • planos das DMVs (nesse caso, eles devem ter apenas os valores de parâmetro compilados, que não são valores de tempo de execução).

Minha resposta se concentra em obter os valores dos parâmetros para outras sessões em execução no momento. Ao confiar nas DMVs, não há como saber se o valor do parâmetro de tempo de execução é o mesmo que o valor do parâmetro compilado. E o contexto dessa pergunta é rastrear o valor do tempo de execução das consultas enviadas por outras sessões / SPIDs (e no SQL Server 2005, enquanto os Eventos estendidos foram introduzidos no SQL Server 2008).

Solomon Rutzky
fonte
13

Você pode ativar o plano de execução real e, em seguida, examinar o XML do plano de execução.

insira a descrição da imagem aqui

Ou você pode usar ferramenta de plano de explorador de sentinela sql e veja o parametersguia que irá listar o compiled valuee run time valuepara o plano de execução real.

Se você não conseguir ativar o plano real, poderá procurar no cache do plano, conforme descrito abaixo.

-- 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
fonte
5
O cache do plano possui apenas os valores compilados em vez dos valores para uma execução específica posteriormente. Também poderia usar o Showplan XML Statistics Profileevento no Profiler para obter o plano real, embora, se o Profiler fosse implementado, haveria maneiras menos intensivas de obter isso.
Martin Smith
1

@SolomonRutzky está certo.
O rastreio do SQL Profiler é a única maneira ( sem editar o Sproc ).

Edite seu Sproc:

No entanto , a próxima melhor coisa é editar um pouco o Sproc em questão.
Declare uma variável DateTime no início com a Hora Atual.
No final do Sproc, registre os valores Sproc_StartTime, Sproc_EndTime e Parameter em uma tabela.

Você pode até adicionar alguma lógica condicional para usar um DateDiff () para registrar apenas quando uma quantidade prolongada de tempo foi usada no processamento do Sproc.
Isso pode acelerar o seu Sproc e reduzir o consumo de espaço da sua tabela de logs para quando o Sproc estiver em execução.

Então você tem um arquivo de log que pode consultar e analisar ao longo de meses (sem um rastreamento em execução no Prod).
Quando terminar de ajustar o Sproc, exclua as poucas linhas da lógica do Timer e Logger que você adicionou.

Valores dos parâmetros do plano em cache:

Devo mencionar que a inclusão dos valores atuais dos parâmetros do plano em cache na tabela de log pode ajudá-lo a determinar se eles estão agravando o problema de desempenho .
Eu uso OPTIMIZE FORpara definir como lidar com os Parâmetros no meu Sproc quando sei que ele será usado para fatiar e cortar dados.
Acho que o uso OPTIMIZE FORgera resultados consistentes e rápidos ao usar o mesmo Sproc com parâmetros que os filtros opcionais .
Definitivamente, é uma variável a menos a considerar se você especificar como lidar com elas.

Abaixo está um exemplo do que você pode adicionar na parte inferior da sua instrução 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))
MikeTeeVee
fonte
0

Notei ao usar a consulta de Erland Sommarskog para fragmentar o XML do plano e extrair ParameterCompiledValue que o primeiro CTE "baseado em dados" não leva em conta planos que possuem WARNINGS (por exemplo, conversões implícitas) como o CHARINDEX (função interna) procura pela primeira expressão que corresponde à string input (ie) e esses avisos usam essas mesmas frases / nós.

Por isso, proponho substituir esta seção pela seção revisada abaixo:

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

Seção revisada:

       CHARINDEX('<ParameterList><ColumnReference', qp.query_plan) + LEN('<ParameterList>') AS paramstart,
       CHARINDEX('</ParameterList></QueryPlan>', qp.query_plan) AS paramend
SQLcyclopedia
fonte
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
fonte
2
As respostas apenas de código são desencorajadas. Considere adicionar uma explicação de por que esse código resolve um problema. Veja como responder
Peter Vandivier