web-dev-qa-db-ja.com

sys.dm_exec_requests-start_time

私が担当しているデータベースのクエリの状態を示す次のクエリがあります(ただし、私はDBAではありません)。

SELECT 
     T3.FullStatement as FullSQLStatement
    ,T3.ExecutingStatement
    ,req.session_id as SessionId
    ,T2.login_name as LoginName
    ,command as SQLCommand
    ,start_time as StartTime
    ,DateDiff(MINUTE,start_time,GetDate()) as ElapsedTimeMinutes
    ,req.status as QueryStatus
    ,req.wait_type as WaitType
    ,req.wait_time as WaitTimeMs
    ,blocking_session_id as BlockingSessionId
    ,req.row_count as [RowCount]
    ,req.cpu_time as CpuTimeMs
    ,req.total_elapsed_time as TotalElapsedTimeMs
    ,SubString(sqltext.TEXT,req.statement_start_offset,req.statement_end_offset-req.statement_start_offset)
FROM sys.dm_exec_requests req
    Inner Join sys.dm_exec_sessions T2 ON T2.session_id = req.session_id
    Cross Apply dbo.GetExecutingSQLStatement (req.session_id) T3
    Cross Apply sys.dm_exec_sql_text(sql_handle) AS sqltext
where req.database_id = 5
Order By 6 Desc

これは、部分的には次のような出力を生成します。

enter image description here 含めた例ではうまく表示されていませんが、start_timeはステートメント全体の開始時間であり、ステートメントの実行部分ではありません。完全なステートメントの開始時間とともに実行部分の開始時間を取得できる場所はありますか?完全なステートメントには個別のクエリが多数含まれる可能性があるため、これは私にとって重要です。

7
Randy Minder

現時点では、これは取得する可能性のある値のようには見えません。これは、持っていると便利な値のように見えるため、残念です。

わかりました(ほとんど)。 sys.dm_exec_query_stats DMVには_last_execution_time_フィールドがありますが、このDMVには次の警告が付属しています。

sys.dm_exec_query_statsの初期クエリは、サーバーで現在実行中のワークロードがある場合、不正確な結果を生成する可能性があります。クエリを再実行することで、より正確な結果を判断できます。

また、_last_execution_time_はクエリバッチごとであり、ではないため、_sys.dm_exec_query_stats_の値が複数のセッションにわたってまったく同じSQLの複数の同時実行によってどのように影響を受けるかはわかりませんセッションごと。

以下はこれまでのところです。クリーンアップしたいことが1つあるかもしれませんが、これは_sys.dm_exec_query_stats_に対応するエントリがないバッチ内の最初のクエリをすでに説明しているはずです(理想的には、常にではありませんが方法)現在のステートメント/クエリが完了した後。また、単一クエリのバッチでも機能するようですので、試してみてください。

_SELECT req.session_id AS [SessionID],
       req.start_time AS [BatchStartTime],
       req.total_elapsed_time as [BatchMilliseconds], --[TotalElapsedTimeMs],
       CASE WHEN stat.last_execution_time IS NOT NULL THEN
                DATEADD(MILLISECOND,
                        (stat.last_elapsed_time / 1000.0),
                        stat.last_execution_time)
            ELSE req.start_time END AS [StatementStartTime],
       DATEDIFF(MILLISECOND,
                CASE WHEN stat.last_execution_time IS NOT NULL THEN
             DATEADD(MILLISECOND, (stat.last_elapsed_time / 1000.0),
                stat.last_execution_time)
                  ELSE req.start_time END, GETDATE()) AS [StatementMilliseconds],
       T2.login_name AS [LoginName],
       req.command AS [SQLCommand],
       req.[status] AS [QueryStatus],
       req.wait_type AS [WaitType],
       req.wait_time AS [WaitTimeMs],
       blocking_session_id AS [BlockingSessionId],
       req.row_count AS [RowCount],
       req.cpu_time AS [CpuTimeMs],
       sqltext.[text] AS [QueryBatch],
       SUBSTRING(sqltext.[text],
                 req.statement_start_offset / 2,
                 CASE req.statement_end_offset
                      WHEN -1 THEN DATALENGTH(sqltext.[text])
                      ELSE (req.statement_end_offset - req.statement_start_offset) / 2
                 END) AS [CurrentQuery]
FROM sys.dm_exec_requests req
INNER JOIN sys.dm_exec_sessions T2
        ON T2.session_id = req.session_id
LEFT JOIN sys.dm_exec_query_stats stat
        ON stat.[sql_handle] = req.[sql_handle]
       AND stat.statement_end_offset = (req.statement_start_offset - 2)
CROSS APPLY sys.dm_exec_sql_text(req.[sql_handle]) sqltext
-- WHERE req.session_id = 56
ORDER BY req.start_time;
_

私はあなたのクエリ(少し変更された)と次のSQLを別のタブ/セッション(TOP(1)TOP(2)など)、および_10.1_、_10.2_などは、WAITFORの時点で、現在のステートメントフィールドを見たときに、どのステートメントが処理されているかを簡単に確認できるようにします)。

_SELECT GETDATE() AS [StartTime];
GO
SELECT TOP(1) [name] FROM sys.objects;
WAITFOR DELAY '00:00:10.1';

SELECT TOP(2) [name] FROM sys.objects;
WAITFOR DELAY '00:00:10.2';

SELECT TOP(3) [name] FROM sys.objects;
WAITFOR DELAY '00:00:10.3';

SELECT TOP(4) [name] FROM sys.objects;
WAITFOR DELAY '00:00:10.4';
_

次に、クエリを数回実行するだけで、_dm_exec_requests.start_time_と_dm_exec_sessions.last_request_start_time_が次のようになることは明らかです。

  • 同じ値、および
  • そのバッチの実行中はまったく変更しないでください。

私はCROSS APPLY sys.dm_exec_text_query_plan(req.plan_handle, req.statement_end_offset, req.statement_end_offset) tplanを介して実行プランを取得しようとしましたが、それはNULLであったため、あまり役に立ちませんでした。


クエリでテストするために、最初の2つの列と_dbo.GetExecutingSQLStatement_関数への参照を削除する必要がありました。これはおそらく、このクエリにCROSS APPLY sys.dm_exec_sql_text(req.[sql_handle])を介してすでにロジックがあるため、取り除くこともできるものです。次の3つの変更を行うだけです。

  • _T3.FullStatement_(選択フィールド#1)は_sqltext.[text]_になります

  • _T3.ExecutingStatement_(選択フィールド#2)はSUBSTRING(sqltext.[text], req.statement_start_offset / 2, CASE req.statement_end_offset WHEN -1 THEN LEN(sqltext.[text]) ELSE (req.statement_end_offset - req.statement_start_offset) / 2 END)になります

  • SUBSTRING(現在の最後のフィールド)を削除します。 SUBSTRINGを注意深く見れば、私は真上に提案しているのですが、それは基本的に最後に持っていたものであり、2つの誤りが修正されることを期待しています。

    • バッチにクエリが1つしかない場合、SUBSTRINGは考慮しませんでした。この場合、_statement_end_offset_は_-1_であり、部分文字列はエラーになります
    • あなたのSUBSTRINGは、2バイトのバイト数から文字数への変換を考慮していません。

しかし、私は余談です。


8
Solomon Rutzky