私たちのETLフローには長時間実行されるSELECT INTOステートメントがあり、それはその場でテーブルを作成し、それに数億のレコードを投入します。
ステートメントはSELECT ... INTO DestTable FROM SrcTable
のようになります。
監視の目的で、実行中のこのステートメントの進行状況の概算を取得します(およその行数、書き込まれたバイト数など)。
次のことを試みたが、役に立たなかった。
-- Is blocked by the SELECT INTO statement:
select count(*) from DestTable with (nolock)
-- Returns 0, 0:
select rows, rowmodctr
from sysindexes with (nolock)
where id = object_id('DestTable')
-- Returns 0:
select rows
from sys.partitions
where object_id = object_id('DestTable')
さらに、sys.dm_tran_active_transactions
でトランザクションを確認できますが、特定のtransaction_id
で影響を受けた行の数を取得する方法を見つけることができませんでした(おそらく@@ROWCOUNT
に似ていますが、 transaction_id
を引数として)。
SQL Serverでは、SELECT INTOステートメントがDDLステートメントとDMLステートメントの両方であるということを理解しています。そのため、暗黙的なテーブル作成はロック操作になります。ステートメントの実行中に何らかの進行情報を取得するための巧妙な方法がまだあると私はまだ思います。
まだコミットされていないため、sys.partitions
のrows
は0だと思います。ただし、これは、トランザクションがコミットした場合にSQL Serverが何を実行するかを認識していないことを意味するものではありません。重要なのは、操作のCOMMITまたはROLLBACKに関係なく、すべての操作が最初にバッファプール(つまりメモリ)を通過することを覚えておくことです。したがって、その情報についてsys.dm_os_buffer_descriptors
を調べることができます。
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT --OBJECT_NAME(sp.[object_id]) AS [TableName], sdobd.*, '---', sp.*, '---', sau.*
SUM(sdobd.[row_count]) AS [BufferPoolRows],
SUM(sp.[rows]) AS [AllocatedRows],
COUNT(*) AS [DataPages]
FROM sys.dm_os_buffer_descriptors sdobd
INNER JOIN sys.allocation_units sau
ON sau.[allocation_unit_id] = sdobd.[allocation_unit_id]
INNER JOIN sys.partitions sp
ON ( sau.[type] = 1
AND sau.[container_id] = sp.[partition_id]) -- IN_ROW_DATA
OR ( sau.[type] = 2
AND sau.[container_id] = sp.[hobt_id]) -- LOB_DATA
OR ( sau.[type] = 3
AND sau.[container_id] = sp.[partition_id]) -- ROW_OVERFLOW_DATA
WHERE sdobd.[database_id] = DB_ID()
AND sdobd.[page_type] = N'DATA_PAGE'
AND sp.[object_id] = (SELECT so.[object_id]
FROM sys.objects so
WHERE so.[name] = 'TestDump')
詳細を表示するには、SELECT
リストの最初の行のコメントを外し、残りの3行をコメント化します。
1つのセッションで以下を実行し、別のセッションで上記のクエリを繰り返し実行してテストしました。
SELECT so1.*
INTO dbo.TestDump
FROM sys.objects so1
CROSS JOIN sys.objects so2
CROSS JOIN sys.objects so3;
監視の目的で、このステートメントの実行中に、ステートメントの進行状況の大まかな概要を知りたいと思います。
単発か継続中か?
これが事前に予想できるニーズである場合* sys.dm_exec_query_profiles
を使用できます
SET STATISTICS XML ON
SELECT so1.*
INTO dbo.TestDump
FROM sys.all_objects so1
CROSS JOIN sys.all_objects so2
CROSS JOIN sys.all_objects so3
CROSS JOIN sys.all_objects so4
CROSS JOIN sys.all_objects so5;
select row_count
from sys.dm_exec_query_profiles
WHERE physical_operator_name = 'Table Insert'
AND session_id = 55;
SELECT INTO
が 並列処理を使用 の場合、返される行数を合計する必要がある場合があります。
*このDMVを使用して監視するセッションでは、SET STATISTICS PROFILE ON
またはSET STATISTICS XML ON
を使用した統計収集を有効にする必要があります。 SSMSから「実際の」実行計画を要求することもできます(後者のオプションが設定されているため)。
行数を取得する方法はないと思いますが、以下を参照することで書き込まれたデータの量を見積もることができます。
SELECT writes
FROM sys.dm_exec_requests WHERE session_id = <x>;
SELECT COUNT(*) FROM sys.dm_db_database_page_allocations
(<dbid>, OBJECT_ID(N'dbo.newtablename'), 0, NULL, 'LIMITED');
完了時にヒープがいくつのページを占有する必要があるかについて何らかの考えがある場合は、完了率を計算できるはずです。後者のクエリは、テーブルが大きくなると高速になりません。そして、おそらく上記をREAD UNCOMMITTED
の下で実行するのが最も安全です(そして、私は何のためにもそれをお勧めすることはあまりありません)。
INSERT
を
_SELECT ... INTO DestTable FROM SrcTable
_
に
_INSERT DestTable SELECT ... FROM SrcTable
_
次に、select count(*) from DestTable with (nolock)
クエリが機能します。
これが不可能な場合は、sp_WhoIsActive(またはDMVの詳細)を使用して、クエリが行う書き込み数を監視できます。これはかなり大まかなゲージになりますが、通常行う書き込み数をベースライン化する場合に役立ちます。
WITH (TABLOCK)
を追加すると、上記のINSERT
で 最小限のロギング を取得できるはずです。