SELECT INTOステートメントの進行


14

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ステートメントは1つのDDLステートメントとDMLステートメントの両方であるため、暗黙的なテーブル作成はロック操作になります。私はまだ、ステートメントの実行中に何らかの種類の進捗情報を取得するための賢い方法が必要だと思います。


グローバル一時テーブル## TABLEを使用した場合、## TABLEのインデックス列でカウント付き選択を実行して、すでに書き込まれたレコードの数を取得し、書き込みの合計レコード数を概算できますか?
CoveGeek

回答:


6

rowsin sys.partitionsはまだコミットされていないため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行をコメント化します。

あるセッションで次を実行し、別のセッションで上記のクエリを繰り返し実行してテストしました。

SELECT so1.*
INTO   dbo.TestDump
FROM   sys.objects so1
CROSS JOIN sys.objects so2
CROSS JOIN sys.objects so3;

1
これは創造的です。大きなバッファプールの列挙が非常に遅いという警告を追加したいだけです。
usr

1
これは、バッファプールからページがまだ削除されていないことを前提としています。
マーティンスミス

@MartinSmithコミット前にページを削除できますか?
ソロモンラッツキー

5
@srutzky-はい。トランザクションログには、ロールバックに必要なすべての情報が含まれています。汚れたページはディスクに書き込むことができます-たとえばチェックポイントで、または特にこの場合はイーガーライターによって、その後バッファプールから削除されます。
マーティンスミス

7

監視目的で、実行中にこのステートメントの進行状況の大まかなアイデアを取得したいと思います。

1回限りですか?

これが事前に予測できる必要がある場合* sys.dm_exec_query_profiles

接続1(セッション55)

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;

接続2

select row_count
from sys.dm_exec_query_profiles
WHERE physical_operator_name = 'Table Insert' 
    AND session_id = 55;

次の場合返された行数合計する必要があるかもしれませんSELECT INTOされて並列処理を使用します

* SET STATISTICS PROFILE ONまたはを使用して統計情報を収集するには、このDMVを使用して監視するセッションを有効にする必要がありますSET STATISTICS XML ON。SSMSから「実際の」実行計画を要求することも機能します(後者のオプションを設定するため)。


2月にこれを+1するのを忘れたようですが、完全に忘れていませんでした:)。OPが少なくとも2014年にあるので、私はこの関連する質問でそれを利用しました:dba.stackexchange.com/questions/139191/…これを指摘してくれてありがとう。非常に便利なDMVです:-)
ソロモンラッツキー

2
@srutzkyうん、それはとても便利です。そして、SSMS 2016のライブ実行計画で利用しましたmsdn.microsoft.com/en-gb/library/dn831878.aspx
マーティンスミス

5

行数を取得する方法はないと思いますが、以下を見ると、書き込まれたデータの量を見積もることができます。

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(そして、私はそれをお勧めしません)。


4

INSERTから変更できる場合

SELECT ... INTO DestTable FROM SrcTable

INSERT DestTable SELECT ... FROM SrcTable

その後、select count(*) from DestTable with (nolock)クエリが機能します。

これが不可能な場合は、sp_WhoIsActiveを使用して(またはDMVを掘り下げて)クエリの書き込み回数を監視できます。これはかなり大まかなゲージになりますが、通常の書き込み回数をベースにした場合に役立ちます。

上記を追加すると、最小限のロギングを取得できるはずINSERTですWITH (TABLOCK)


このコメントをありがとう。最小限のロギングを取得したいので、SELECT ... INTOアプローチを使用している理由です(また、私たちは怠け者だからです)
ダン

1
あなたが最低限のログを取得することができる必要がありINSERT、あなたが追加した場合、上記WITH(TABLOCK)
ジェームズ・アンダーソン

@JamesAnderson-テーブルがヒープとして残っている場合、これはBULK_OPERATIONロックを取得するため、再びブロッキングを引き起こします。
マーティンスミス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.