web-dev-qa-db-ja.com

クエリを担当するユーザー名またはIPアドレス、あるいはその両方を取得する

次のクエリを使用して、クエリのパフォーマンスの向上を見つけます。

SELECT TOP 20 SUBSTRING(qt.text, (qs.statement_start_offset/2)+1, 
        ((CASE qs.statement_end_offset
          WHEN -1 THEN DATALENGTH(qt.text)
         ELSE qs.statement_end_offset
         END - qs.statement_start_offset)/2)+1),
  qs.execution_count,
  qs.total_logical_reads, qs.last_logical_reads, qs.min_logical_reads,
  qs.max_logical_reads, qs.total_physical_reads, qs.last_physical_reads,
  qs.min_physical_reads, qs.max_physical_reads,
  qs.total_elapsed_time / 1000000 As total_elapsed_time, 
  qs.last_elapsed_time / 1000000 As last_elapsed_time,
  qs.min_elapsed_time / 1000000 As min_elapsed_time, 
  qs.max_elapsed_time / 1000000 As max_elapsed_time,
  qs.last_execution_time, qs.creation_time, qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
WHERE qt.encrypted=0
AND last_execution_time >= DATEADD (MINUTE , -5 , CURRENT_TIMESTAMP )
ORDER BY qs.total_logical_reads DESC
--ORDER BY qs.total_physical_reads DESC

問題は、ときどき繰り返しスローされるように見え、拡張が必要な​​クエリを見つけたが、それがどこから来たのかを判断できないことです(ユーザーまたはプログラムである可能性がありますが、どのような場合でもログインまたはユーザー名を持っている)。このユーザーを取得したり、このクエリを追跡して、改善を適用するにはどうすればよいですか?

5
Hauri

あなたの最善の策は、拡張イベントかトレースのどちらかだと思います。残念ながら、拡張イベントについて十分な知識がありませんので、そこでアドバイスを提供することはできますが、トレースの設定についていくつか説明できます。

まず第一に、トレースは、とりわけホスト名、つまりクエリの送信元のサーバー/ワークステーションをプルできます。ユーザー名が実際には役に立たない場合があり、実行しているマシンによって誰が何をしているのかを頻繁に追跡できるため、これを含めます。

次に、「textdata」のフィルターがあり、最初に検索するクエリの文字数が多いクエリのみをプルできるはずです。ただし、少し遅くなる場合があります。キャリッジリターンやラインフィードなどの書式設定によって、出力が失われるかどうかはわかりません。 x期間以上、x読み取り以上、x書き込み以上などのクエリのみをプルするトレースを実行できます。上記のクエリからの情報を使用することにより、「誤検知」応答の数を維持できるはずです。かなり低い。これらのタイプの制限を使用すると、トレースのオーバーヘッドがかなり低くなります。

編集:使用できるトレースを作成するスクリプトの例を次に示します。私はあなたが必要だと思うすべてのものにコメントしました。ファイルのスクリプトを作成すると、GUIを実行するよりもオーバーヘッドが大幅に少なくなります。定期的に作成されたファイルを処理して削除するようにポイントを作成するだけです。そして、トレースが完了したら、必ずシャットダウンしてください。

Trace createスクリプトの下にスクリプトを含めて、使い終わったらシャットダウンできるようにします。

-- Create a Queue
declare @rc int
declare @TraceID int
declare @maxfilesize bigint
set @maxfilesize = 5

-- Note that this will start a new trace file each time the old ones get to 5mb.  
-- This way you can process and remove the older files.  Be careful to shut down your
-- trace when you are done as a runaway trace can use up all the space on your drive.
exec @rc = sp_trace_create @TraceID output, 0, N'C:\TraceFiles\QueryTrace', @maxfilesize, NULL
if (@rc != 0) goto error

-- Set the events
declare @on bit
set @on = 1
-- Here are the events the trace will look for along with what data
-- will be pulled for each event.  I've labeled the events by group
-- and for the first event I've labeled each of the data points.
-- Look up sp_trace_setevent in BOL for further descriptions.

-- Remote procedure call completed
exec sp_trace_setevent @TraceID, 10, 15, @on -- EndTime
exec sp_trace_setevent @TraceID, 10, 8, @on -- HostName
exec sp_trace_setevent @TraceID, 10, 16, @on -- Reads
exec sp_trace_setevent @TraceID, 10, 1, @on -- TextData
exec sp_trace_setevent @TraceID, 10, 9, @on -- ClientProcessId
exec sp_trace_setevent @TraceID, 10, 17, @on -- Writes
exec sp_trace_setevent @TraceID, 10, 10, @on -- ApplicationName
exec sp_trace_setevent @TraceID, 10, 18, @on -- CPU
exec sp_trace_setevent @TraceID, 10, 34, @on -- ObjectName
exec sp_trace_setevent @TraceID, 10, 3, @on -- DatabaseId
exec sp_trace_setevent @TraceID, 10, 11, @on -- LoginName
exec sp_trace_setevent @TraceID, 10, 12, @on -- SPID
exec sp_trace_setevent @TraceID, 10, 13, @on -- Duration
exec sp_trace_setevent @TraceID, 10, 6, @on -- NTUserName
exec sp_trace_setevent @TraceID, 10, 14, @on -- StartTime
-- SQL Batch completed
exec sp_trace_setevent @TraceID, 12, 15, @on
exec sp_trace_setevent @TraceID, 12, 8, @on
exec sp_trace_setevent @TraceID, 12, 16, @on
exec sp_trace_setevent @TraceID, 12, 1, @on
exec sp_trace_setevent @TraceID, 12, 9, @on
exec sp_trace_setevent @TraceID, 12, 17, @on
exec sp_trace_setevent @TraceID, 12, 6, @on
exec sp_trace_setevent @TraceID, 12, 10, @on
exec sp_trace_setevent @TraceID, 12, 14, @on
exec sp_trace_setevent @TraceID, 12, 18, @on
exec sp_trace_setevent @TraceID, 12, 3, @on
exec sp_trace_setevent @TraceID, 12, 11, @on
exec sp_trace_setevent @TraceID, 12, 12, @on
exec sp_trace_setevent @TraceID, 12, 13, @on
-- SQL Statement completed
exec sp_trace_setevent @TraceID, 41, 15, @on
exec sp_trace_setevent @TraceID, 41, 8, @on
exec sp_trace_setevent @TraceID, 41, 16, @on
exec sp_trace_setevent @TraceID, 41, 1, @on
exec sp_trace_setevent @TraceID, 41, 9, @on
exec sp_trace_setevent @TraceID, 41, 17, @on
exec sp_trace_setevent @TraceID, 41, 6, @on
exec sp_trace_setevent @TraceID, 41, 10, @on
exec sp_trace_setevent @TraceID, 41, 14, @on
exec sp_trace_setevent @TraceID, 41, 18, @on
exec sp_trace_setevent @TraceID, 41, 3, @on
exec sp_trace_setevent @TraceID, 41, 11, @on
exec sp_trace_setevent @TraceID, 41, 12, @on
exec sp_trace_setevent @TraceID, 41, 13, @on

-- Set the Filters
declare @intfilter int
declare @bigintfilter bigint

-- Here are the filters.  The more specific you can be the less overhead.
-- In your case you are working with a "problem" query so at least one of 
-- the following should be an issue:  duration, reads, writes.  With any
-- luck you don't have a lot of high resource queries on your instance
-- so these filters won't pull a tremendous amount more than what you 
-- are looking for.  

-- Obviously you will need to modify the filter values to fit your purpose.

-- Filter by database id
set @intfilter = 17
exec sp_trace_setfilter @TraceID, 3, 0, 0, @intfilter

-- Filter by duration. Greater than or equal to.
set @bigintfilter = 10000000
exec sp_trace_setfilter @TraceID, 13, 0, 4, @bigintfilter

-- Filter by reads. Greater than or equal to.
set @bigintfilter = 1000
exec sp_trace_setfilter @TraceID, 16, 0, 4, @bigintfilter

-- Filter by writes. Greater than or equal to.
set @bigintfilter = 1000
exec sp_trace_setfilter @TraceID, 17, 0, 4, @bigintfilter

-- This is generated by the GUI to filter out the profiler.
-- I'm striping off the GUID on the end as that would be 
-- specific to my machine (I believe).
-- exec sp_trace_setfilter @TraceID, 10, 0, 7, N'SQL Server Profiler - 99514c1d-7404-4448-8831-1225125c45aa'
exec sp_trace_setfilter @TraceID, 10, 0, 7, N'SQL Server Profiler'

-- Set the trace status to start
exec sp_trace_setstatus @TraceID, 1

-- display trace id for future references
select TraceID=@TraceID
goto finish

error: 
select ErrorCode=@rc

finish: 
go

完了したらトレースをシャットダウンするスクリプト。

-- This query pulls a list of all traces.
-- You will be able to tell which one is yours
-- by the filename you selected when you
-- created it.
SELECT * FROM sys.fn_trace_getinfo(NULL)
-- Get the trace id from the query above and use it instead of @TraceID.
exec sp_trace_setstatus @TraceID, 0
exec sp_trace_setstatus @TraceID, 2
6
Kenneth Fisher

以下のクエリを使用して、現在実行中のリクエストおよび対応するセッション/接続情報をプルできます。

select
    r.session_id,
    s.login_name,
    c.client_net_address,
    s.Host_name,
    s.program_name,
    st.text
from sys.dm_exec_requests r
inner join sys.dm_exec_sessions s
on r.session_id = s.session_id
left join sys.dm_exec_connections c
on r.session_id = c.session_id
outer apply sys.dm_exec_sql_text(r.sql_handle) st
--where st.text like '%your query string to search for%';

質問で何を照会しているか、sys.dm_exec_query_statsは、セッション/接続ではない累積および集約された統計です。つまり、その情報はそこには保存されず、簡単に取得することもできません。

SQLトレースまたはXEセッションを配置して、探しているこのデータを取得し、基本的には問題のあるクエリの発生を監視することができます。

探しているデータを取得する方法はいくつかありますが、残念ながら、指定したクエリでは、それを探しているものに簡単に変換する方法はありません。単純に異なるタイプのデータ(集計データを提供しますが、スナップショットと詳細データを探しています)。

10
Thomas Stringer