web-dev-qa-db-ja.com

SQL Serverで、実行中のストアドプロシージャに渡されるパラメーターの値を決定する方法はありますか

実行中のストアドプロシージャを特定する1つの方法は、次のように「動的管理」メソッドを使用することです。

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 BertrandDBCC InputBuffer について言及していますが、この問題には適切ではないと思います。)

13
user420667

この情報(ストアドプロシージャ(つまり、RPC呼び出し)またはパラメーター化されたクエリに渡されるランタイムパラメーター値)は、SQLトレースを介してのみ利用できます(SQL Serverの新しいバージョンでは、同等の拡張イベントを想定しています)。これは、SQL Serverプロファイラ(SQL Serverに付属)を実行し、RPC:CompletedSP:CompletedSQL:BatchCompletedなどのさまざまな「完了」イベントを選択することで確認できます。また、値がそこにあるので、「TextData」フィールドを選択する必要があります。

この質問に対する私の回答と@Kinの answer の違いは、@ Kinの回答(私が間違っている場合を除き、この場合は削除します)は次のいずれかを取得することに重点を置いていることです。

  • 独自のクエリプラン(この場合、ランタイムパラメーター情報を含めることができますが、他のセッション/ SPIDについては含めることができません)、または
  • dMVからの計画(この場合、計画は、ランタイム値ではないコンパイル済みパラメーター値のみを持つ必要がありますです)。

私の答えは、現在実行中のotherセッションのパラメーター値を取得することに焦点を当てています。 DMVに依存している場合、ランタイムパラメーター値がコンパイル済みパラメーター値と同じであるかどうかを知る方法はありません。この質問のコンテキストは、他のセッション/ SPIDを介して送信されるクエリの実行時の値を追跡することです(SQL Server 2005では拡張イベントがSQL Server 2008で導入されました)。

16
Solomon Rutzky

実際の実行プランをオンできる後、実行プランXMLを確認します。

enter image description here

または、 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
13
Kin Shah

@SolomonRutzkyは正しいです。
SQLプロファイラトレースが唯一の方法です(Sprocを編集せずに)。

Sprocを編集します。

ただし、次善の策は問題のSprocを少し編集することです。
DateTime変数を現在の時刻で最初に宣言します。
Sprocの最後に、Sproc_StartTime、Sproc_EndTime、およびパラメーター値をテーブルに記録します。

Sprocの処理に長い時間が使用された場合にのみ、ロギングにDateDiff()を使用する条件付きロジックを追加することもできます。
これにより、Sprocが高速化し、Sprocが最新の状態で実行されている場合のログテーブルのスペース消費量が削減されます。

次に、数か月かけてクエリを実行して分析できるログファイルがあります(製品でトレースを実行していません)。
Sprocの調整が完了したら、追加したタイマーおよびロガーロジックの数行を削除するだけです。

キャッシュされたプランのパラメーター値:

ログテーブルに現在のキャッシュプランのパラメーター値を含めると、パフォーマンスの問題を複雑にしているかどうかを判断するのに役立つことがあります
私が使う OPTIMIZE FORは、データのスライスとダイシングに使用されることがわかっている場合に、Sprocでパラメーターを処理する方法を設定します。
私はOPTIMIZE FORは、パラメーターと同じSprocをオプションのフィルターとして使用すると、一貫した高速な結果を生成します。
それらの処理方法を指定する場合、考慮する変数が1つ少なくなることは間違いありません。

以下は、選択ステートメントの下部に追加する可能性のある例です。

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))
1
MikeTeeVee

Erland Sommarskogのクエリを使用してプランXMLを細断し、ParameterCompiledValueを引き出すときに、最初の "basedata" CTEが[〜#〜] warnings [〜#〜](eg暗黙的)のプランを考慮していないことに気付きました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
0
SQLcyclopedia