ネイティブSQLの大規模なINSERT操作でバッチコミットしますか?
-
05-07-2019 - |
質問
ビューからデータを取り込む必要があるいくつかの大きなテーブル(188mおよび144m行)がありますが、各ビューには数億行が含まれています(疑似次元でモデル化されたデータをフラットな形にまとめます)。各テーブルのキーは、50を超える列の複合バイトです。データがテーブルにある場合、sp_renameを使用して他の新しいテーブルを作成することを常に考えることができましたが、それは実際にはオプションではありません。
単一のINSERT操作を実行すると、プロセスは大量のトランザクションログスペースを使用します。通常、それをファイルし、DBAとの面倒な作業を促します。 (そして、はい、これはおそらくDBAが処理/設計/アーキテクトするべき仕事です)
SSISを使用して、バッチコミットで宛先テーブルにデータをストリーミングできます(ただし、サーバーでSSISパッケージを実行することは許可されていないため、ネットワーク経由でデータを送信する必要があります)。
プロセスを複数のINSERT操作に分割し、ある種のキーを使用して行を異なるバッチに分散し、ループを実行する以外のことは何ですか?
解決
データをパーティション分割して、カーソルループにデータを挿入できます。これは、SSISバッチ挿入とほぼ同じです。ただし、サーバーで実行されます。
create cursor ....
select YEAR(DateCol), MONTH(DateCol) from whatever
while ....
insert into yourtable(...)
select * from whatever
where YEAR(DateCol) = year and MONTH(DateCol) = month
end
他のヒント
ビューには、あらゆる種類の一意の識別子/候補キーがありますか?その場合、次を使用してこれらの行を作業テーブルに選択できます。
SELECT key_columns INTO dbo.temp FROM dbo.HugeView;
(それが理にかなっている場合は、このテーブルを、おそらくSIMPLE復旧モデルを使用して別のデータベースに入れて、ログアクティビティがプライマリデータベースに干渉するのを防ぐことができます。問題がすべてのディスク領域が不十分な場合に備えて、再開する前に他のデータベースの領域を確保してください。)
この場合、一度に10,000行を挿入し、その間にログインをバックアップして、次のようなことができます。
SET NOCOUNT ON;
DECLARE
@batchsize INT,
@ctr INT,
@rc INT;
SELECT
@batchsize = 10000,
@ctr = 0;
WHILE 1 = 1
BEGIN
WITH x AS
(
SELECT key_column, rn = ROW_NUMBER() OVER (ORDER BY key_column)
FROM dbo.temp
)
INSERT dbo.PrimaryTable(a, b, c, etc.)
SELECT v.a, v.b, v.c, etc.
FROM x
INNER JOIN dbo.HugeView AS v
ON v.key_column = x.key_column
WHERE x.rn > @batchsize * @ctr
AND x.rn <= @batchsize * (@ctr + 1);
IF @@ROWCOUNT = 0
BREAK;
BACKUP LOG PrimaryDB TO DISK = 'C:\db.bak' WITH INIT;
SET @ctr = @ctr + 1;
END
これで頭が一杯になりますので、カット/ペースト/実行はしないでください。しかし、一般的な考え方はそこにあると思います。
通常のデータベースとログのバックアップを取得している場合、ログチェーンを最初からやり直すためにフルバックアップを実行する必要があることに注意してください。
これが古いスレッドであることは知っていますが、アーサーのカーソルソリューションの汎用バージョンを作成しました。
--Split a batch up into chunks using a cursor.
--This method can be used for most any large table with some modifications
--It could also be refined further with an @Day variable (for example)
DECLARE @Year INT
DECLARE @Month INT
DECLARE BatchingCursor CURSOR FOR
SELECT DISTINCT YEAR(<SomeDateField>),MONTH(<SomeDateField>)
FROM <Sometable>;
OPEN BatchingCursor;
FETCH NEXT FROM BatchingCursor INTO @Year, @Month;
WHILE @@FETCH_STATUS = 0
BEGIN
--All logic goes in here
--Any select statements from <Sometable> need to be suffixed with:
--WHERE Year(<SomeDateField>)=@Year AND Month(<SomeDateField>)=@Month
FETCH NEXT FROM BatchingCursor INTO @Year, @Month;
END;
CLOSE BatchingCursor;
DEALLOCATE BatchingCursor;
GO
これにより、大きなテーブルのロードに関する問題が解決されました。
ピクシーダストはありません、あなたはそれを知っています。
転送される実際のスキーマについての詳細を知らなくても、一般的な解決策は説明したとおりです。処理を複数の挿入に分割し、キーを追跡します。これは一種の擬似コードT-SQLです:
create table currentKeys (table sysname not null primary key, key sql_variant not null);
go
declare @keysInserted table (key sql_variant);
declare @key sql_variant;
begin transaction
do while (1=1)
begin
select @key = key from currentKeys where table = '<target>';
insert into <target> (...)
output inserted.key into @keysInserted (key)
select top (<batchsize>) ... from <source>
where key > @key
order by key;
if (0 = @@rowcount)
break;
update currentKeys
set key = (select max(key) from @keysInserted)
where table = '<target>';
commit;
delete from @keysInserted;
set @key = null;
begin transaction;
end
commit
並列バッチを許可し、キーをパーティション分割する場合、より複雑になります。
BCPコマンドを使用してデータをロードし、Batch Sizeパラメーターを使用できます
http://msdn.microsoft.com/en-us/library /ms162802.aspx
2段階のプロセス
- ビューからテキストファイルへのBCP OUTデータ
- BCP INデータをテキストファイルからバッチサイズパラメータ付きのテーブルに挿入
これは良い仕事だと思われます BCP 。