SQL Server - 拡張イベントを使ってスロークエリを取得するクエリ
SQL Serverの拡張イベントを手っ取り早く試すにはこの記事が良いかなと思います。
この記事もわかりやすくSSMSのGUI自体もわかりやすいので、簡単に拡張イベントを使えるかなと思います。簡単に使えてしまうのですが、せっかくなのでクエリをそこそこ理解したいところ。今回はGUIを使わずにクエリだけで次のことを試します。
- 拡張イベントのセッションを作成する
- イベントをキャプチャする
- キャプチャしたイベントを確認する
例としてはありがちですが、いわゆるスロークエリを取得してみます。順番に見ていきましょう。
拡張イベントのセッションを作成する
イベントをキャプチャするためにまずはセッションを作成します。
-- セッションを作成 create event session test_xes on server -- sql_batch_completedイベントをキャプチャする add event sqlserver.sql_batch_completed( -- duration(バッチが完了するまでの時間)が3秒以上のイベントにフィルタする where duration >= 3000000 ), -- rpc_completedイベントをキャプチャする add event sqlserver.rpc_completed( where duration >= 3000000 ) -- イベントデータはring_buffer(メモリ)に出力する add target package0.ring_buffer;
add event
句でキャプチャしたい「イベント」を指定します。このクエリではsql_batch_completed
イベント・rpc_completed
イベントの2つをキャプチャするようにしています。where
句は「述語」や「述語フィルタ」と呼ばれる部分です。クエリが完了するまでに3秒以上かかったイベントだけにフィルタをしています。add target
句でイベントデータの出力先である「ターゲット」を指定します。このクエリではリングバッファ(メモリ)を指定しています。
またイベントや述語、ターゲットなどは「パッケージ」に含まれています。このクエリのsqlserver
やpackage0
がパッケージ名です。パッケージは名前空間やコンテナのようなイメージでいいのかなと思います。
参考
イベントをキャプチャする
セッションを開始します。キャプチャを開始するというか、監視を開始するというか。
-- セッション(キャプチャ)を開始 alter event session test_xes on server state = start; -- ちなみに停止するには /* alter event session test_xes on server state = stop; */
セッションが開始されたので、イベントを発生させてキャプチャしたいと思います。そのために次のプログラムを実行します。sql_batch_completed
イベントとrpc_completed
イベントをキャプチャできるように、3秒以上かかるクエリを2つ実行するプログラムです。
class Program { private static readonly string _connectionString = new SqlConnectionStringBuilder { DataSource = ".", IntegratedSecurity = true, }.ToString(); static void Main(string[] args) { using (var connection = new SqlConnection(_connectionString)) { // 3秒以上かかってsql_batch_completedイベントを発生 var result1 = connection.Query<int>( "waitfor delay '00:00:03'; select 100;").First(); Console.WriteLine(result1); // 結果に意味はない // 3秒以上かかってrpc_completedイベントを発生 var result2 = connection.Query<int>( "waitfor delay '00:00:03'; select @p;", new { p = 99 }).First(); Console.WriteLine(result2); // 結果に意味はない } } }
これで2つのイベントがリングバッファに出力されているはずです。
キャプチャしたイベントを確認する
キャプチャしたイベントのイベントデータを確認します。
出力されたイベントデータは、SSMSだとオブジェクトエクスプローラから[管理]-[拡張イベント]-[セッション]-[対象のセッション]の対象ターゲットを選択して[ターゲットデータを表示]すると確認できます。今回の記事ではターゲットはpackage0.ring_buffer
です。
ターゲットデータを表示すると次のようなxmlを確認できます。
<!-- イベントデータ:一部要素や属性を省略 --> <RingBufferTarget> <event name="sql_batch_completed" package="sqlserver" timestamp="2017-08-11T03:54:22.422Z"> <data name="duration"> <type name="uint64" package="package0"></type> <value>3002521</value> </data> <data name="batch_text"> <type name="unicode_string" package="package0"></type> <value><![CDATA[waitfor delay '00:00:03'; select 100;]]></value> </data> </event> <event name="rpc_completed" package="sqlserver" timestamp="2017-08-11T03:54:25.445Z"> <data name="duration"> <type name="uint64" package="package0"></type> <value>3001218</value> </data> <data name="statement"> <type name="unicode_string" package="package0"></type> <value><![CDATA[exec sp_executesql N'waitfor delay ''00:00:03''; select @p;',N'@p int',@p=99]]></value> </data> </event> </RingBufferTarget>
これをクエリで確認するには、次のようなクエリを実行します。
select cast(xe_st.target_data as xml) -- データ(xml) from sys.dm_xe_sessions as xe_s inner join sys.dm_xe_session_targets as xe_st on xe_s.address = xe_st.event_session_address where xe_s.name = N'test_xes';
sys.dm_xe_sessionsを使ってアクティブな(=開始されている)セッションを特定できます。セッションが停止中は取得できません。sys.dm_xe_session_targetsを使ってセッションのターゲットを取得でき、target_dataカラムからイベントデータを取得できます。
ここまでくればあとはxmlデータ型のnodesメソッドやvalueメソッドを使って必要な情報を抜き出すだけです。上記クエリと組み合わせて、xmlデータから必要な情報を抜き出すクエリはこんな感じです。
with xe_e(data) as( select cast(xe_st.target_data as xml) -- データ(xml) from sys.dm_xe_sessions as xe_s inner join sys.dm_xe_session_targets as xe_st on xe_s.address = xe_st.event_session_address where xe_s.name = N'test_xes' ) select node.value('@package', 'nvarchar(10)') as package, node.value('@name', 'nvarchar(20)') as event, node.value('@timestamp', 'datetime2') as timestamp, node.value('(data[@name="duration"]/value)[1]', 'bigint') as duration, node.value('(data[@name="batch_text"]/value)[1]', 'nvarchar(max)') as batch_text, node.value('(data[@name="statement"]/value)[1]', 'nvarchar(max)') as statement from xe_e cross apply [data].nodes('/RingBufferTarget/event') as event(node); -- 結果 /* package event timestamp duration batch_text statement ---------- -------------------- --------------------------- -------------------- ---------------------------------------- -------------------------------------------------------------------------------- sqlserver sql_batch_completed 2017-08-11 03:54:22.4220000 3002521 waitfor delay '00:00:03'; select 100; NULL sqlserver rpc_completed 2017-08-11 03:54:25.4450000 3001218 NULL exec sp_executesql N'waitfor delay ''00:00:03''; select @p;',N'@p int',@p=99 */
必要な情報を取得することができました。ただXQueryがよく分からないので手探りです。
参考
T-SQLでn個ずつのグループにわける
前回はn個のグループにわけるクエリを書いてみました。
今回はn個ずつになるようにグルーピングするクエリをROW_NUMBER関数を使って書いてみたいと思います。
ROW_NUMBER (Transact-SQL) | Microsoft Docs
前回と同じように11から20までの数値があるとします。
-- 11から20までの数値があるとして select * from #Sequence; /* Value ----------- 11 12 13 14 15 16 17 18 19 20 */
この10個の数値を4個ずつのグループ(この場合は4個・4個・2個のグループ)にわかれるようにグループ番号を割り振ります。グループ番号はNTILE関数と同じように1からとしてみます。
-- 4個ずつのグループにわける declare @count int = 4; select *, -- グループ番号は1からにする (row_number() over (order By Value) - 1) / @count + 1 as GroupNo -- グループ番号が0からでよければ -- (row_number() over (order By Value) - 1) / @count as GroupNo from #Sequence; /* Value GroupNo ----------- -------------------- 11 1 12 1 13 1 14 1 15 2 16 2 17 2 18 2 19 3 20 3 */
4個ずつのグループにわけることができました。
T-SQLでn個のグループにわける
NTILE関数を使うと簡単にデータをn個のグループにわけることができます。正確には、n個のグループにわけた場合のグループ番号を割り振ると言ったほうがいいかもしれません。
NTILE (Transact-SQL) | Microsoft Docs
NTILE関数を使ったサンプルを書いてみます。
まず適当なデータ、今回は11から20までの10個の数値を用意します。
drop table if exists #Sequence; -- 11から20までの10個の数値を生成 with Source(Value) as( select 11 union all select Value + 1 from Source ) select top 10 * into #Sequence from Source; -- 確認 select * from #Sequence; /* Value ----------- 11 12 13 14 15 16 17 18 19 20 */
この11から20までの数値を3つのグループにわけたいと思います。
-- 3つのグループにわける declare @group int = 3; select *, -- Valueの昇順で3つのグループにわけたグループ番号を割り振る ntile(@group) over (order By Value) as GroupNo from #Sequence; /* Value GroupNo ----------- -------------------- 11 1 12 1 13 1 14 1 15 2 16 2 17 2 18 3 19 3 20 3 */
4個・3個・3個のグループにわけることができました。総数がグループ数で割り切れない場合、NTILE関数は最初のグループから多く割り振っていきます。
SQL Server - 拡張イベントで収集できるイベントデータを確認するクエリ
拡張イベントで収集できるイベントデータを確認するクエリです。
動的管理ビューを3つ使います。sys.dm_xe_packages、sys.dm_xe_objects、sys.dm_xe_object_columnsの3つ。
動的管理ビュー | 概要 |
---|---|
sys.dm_xe_packages (Transact-SQL) | パッケージ一覧。イベントはパッケージによってグルーピングされています。 |
sys.dm_xe_objects (Transact-SQL) | イベント(オブジェクト)一覧。収集したいイベントを確認できます。 |
sys.dm_xe_object_columns (Transact-SQL) | オブジェクトのカラム一覧。イベントが収集されるときにどんなデータが含まれるかを確認できます。イベントデータというかイベントの属性というか。 |
収集できるイベントデータを確認するクエリがこちらです。例としてsql_batch_completed
イベントのイベントデータを確認しています。
-- sql_batch_completedイベントのイベントデータを確認する select xe_oc.name as column_name, xe_oc.column_type as column_type, xe_oc.type_name as type_name, xe_oc.description as column_description from sys.dm_xe_packages as xe_p inner join sys.dm_xe_objects as xe_o on xe_p.guid = xe_o.package_guid inner join sys.dm_xe_object_columns as xe_oc on xe_o.package_guid = xe_oc.object_package_guid and xe_o.name = xe_oc.object_name where -- sys.dm_xe_objectsにはアクション・ターゲットなどイベント以外も含まれるので -- object_type = N'event'でフィルタ xe_o.object_type = N'event' and xe_p.name = N'sqlserver' and xe_o.name = N'sql_batch_completed';
結果です。(空白などは調整してあります。)
column_name column_type type_name column_description ------------------- ------------- ------------------ ------------------------------------------------------------------------------------- UUID readonly guid_ptr グローバル一意 ID VERSION readonly uint8 イベント スキーマのバージョン CHANNEL readonly etw_channel ETW チャネル KEYWORD readonly keyword_map 関連キーワード collect_batch_text customizable boolean 0 に設定すると、collect_batch_text によって batch_text の収集が無効になります。既定では、batch_text 列が生成されます。 cpu_time data uint64 バッチによって使用される CPU 時間 (ミリ秒)。 duration data uint64 バッチ完了にかかった時間 (マイクロ秒)。 physical_reads data uint64 バッチによって発行された、物理ページの読み取り回数。 logical_reads data uint64 バッチによって発行された、論理ページの読み取り回数。 writes data uint64 NULL row_count data uint64 バッチによって返された行数。 result data rpc_return_result バッチの実行結果を示します。 batch_text data unicode_string 実行されているバッチのテキスト。
column_typeがdata
の行が実行時に収集されるイベントデータです。column_typeがcustomizable
の行は収集するかどうかを設定できます。readonly
は固定値?まだよくわからず。
ついでにrpc_completed
イベントのイベントデータも確認しておきます。where句以外は上記と同じクエリです。
-- rpc_completedイベントのイベントデータを確認する select xe_oc.name as column_name, xe_oc.column_type as column_type, xe_oc.type_name as type_name, xe_oc.description as column_description from sys.dm_xe_packages as xe_p inner join sys.dm_xe_objects as xe_o on xe_p.guid = xe_o.package_guid inner join sys.dm_xe_object_columns as xe_oc on xe_o.package_guid = xe_oc.object_package_guid and xe_o.name = xe_oc.object_name where xe_o.object_type = N'event' and xe_p.name = N'sqlserver' and xe_o.name = N'rpc_completed';
結果。
column_name column_type type_name column_description -------------------------- ------------- ------------------------- -------------------------------------------------------------------------------------------------------------- UUID readonly guid_ptr グローバル一意 ID VERSION readonly uint8 イベント スキーマのバージョン CHANNEL readonly etw_channel ETW チャネル KEYWORD readonly keyword_map 関連キーワード collect_statement customizable boolean 0 に設定すると、collect_statement によって statement の収集が無効になります。既定では、statement 列が生成されます。 collect_data_stream customizable boolean 1 に設定すると、collect_data_stream によって data_stream の収集が有効になります。既定では、data_stream 列は生成されません。 collect_output_parameters customizable boolean 1 に設定すると、collect_output_parameters によって output_parameters の収集が有効になります。既定では、output_parameters 列は生成されません。 cpu_time data uint64 NULL duration data uint64 リモート プロシージャ呼び出しが完了するまでにかかった時間 (マイクロ秒)。 physical_reads data uint64 リモート プロシージャ呼び出しによって発行された、物理ページの読み取り回数。 logical_reads data uint64 リモート プロシージャ呼び出しによって発行された、論理ページの読み取り回数。 writes data uint64 NULL result data rpc_return_result リモート プロシージャ呼び出しからの戻り値。 row_count data uint64 リモート プロシージャ呼び出しによって返された行数。 connection_reset_option data connection_reset_option 接続のリセットに使用されたオプション。このフィールドは、接続のリセット リモート プロシージャ呼び出しでのみ使用できます。 object_name data unicode_string リモート プロシージャ呼び出しで参照されていたオブジェクトの名前。 statement data unicode_string リモート プロシージャ呼び出しで実行されたステートメントのテキスト。 data_stream data binary_data トレースの再生に使用される、リモート プロシージャ呼び出しのバイナリ ストリーム。 output_parameters data xml リモート プロシージャ呼び出しから返された出力パラメーター。
SQL Server 2016で確認しています。
SQL Server - トレースイベントに対応する拡張イベントを確認するクエリ
拡張イベントを調べています。勉強した小ネタを残していこうと思います。
今回はトレースイベントに対応する拡張イベントを確認するクエリです。sys.trace_eventsとsys.trace_xe_event_mapを使います。
例として次の2つのトレースイベントに対応する拡張イベントを取得したいとします。
クエリは次のような感じに。
-- トレースイベントに対応する拡張イベントを確認する select te.trace_event_id as te_event_id, -- トレースイベントID te.name as te_event_name, -- トレースイベント名 xe.package_name as xe_package_name, -- 拡張イベントパッケージ名 xe.xe_event_name as xe_event_name -- 拡張イベント名 from sys.trace_events as te inner join sys.trace_xe_event_map as xe on te.trace_event_id = xe.trace_event_id where te.name in (N'RPC:Completed', N'SQL:BatchCompleted'); -- 結果 /* te_event_id te_event_name xe_package_name xe_event_name ------------ -------------------- -------------------- -------------------- 10 RPC:Completed sqlserver rpc_completed 12 SQL:BatchCompleted sqlserver sql_batch_completed */
いずれは拡張イベントでいわゆるスロークエリを取得したいと思っているのでこの2つのイベントを例として選んでみました。