web-dev-qa-db-ja.com

ゆっくり実行しているMERGE

私は[〜#〜] merge [〜#〜]ステートメントを作成して、必要な列の最小限のセットを使用して差分を運用データベースからステージングデータベースに転送しました。マージされたテーブルは、さまざまなレポートおよび分析シナリオで使用される予定です(読み取り専用)。ステージングテーブルに差異を取得するプロセス全体は高速である必要があります(少なくとも、実際の量に関係なく、すべてのデータに対してDELETEに続いてINSERT INTOを実行することにより、テーブル全体を完全に毎日取得する現在の方法よりもはるかに高速です)約5%と推定される変更されたデータ)。

ここにサンプルがあります:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

SET NOCOUNT ON

IF NOT exists(select 1 from StagingDB.sys.tables t join StagingDB.sys.schemas s on t.schema_id = s.schema_id WHERE t.name like 'A' and s.name like 'staging') 
BEGIN
        SELECT Top 0 [ID],[OID]
        INTO [StagingDB].[staging].[A]
        From [LiveDB].[dbo].[A];
END;

MERGE INTO [StagingDB].[staging].[A] AS Target
USING ( 
    SELECT [ID], [OID] From [LiveDB].[dbo].[A] where X = 0
) AS Source ([ID],[OID])
ON (Target.[ID] = Source.[ID])
WHEN MATCHED AND (Target.[OID] <> Source.[OID]) THEN
    UPDATE SET [OID] = Source.[OID]
WHEN NOT MATCHED BY TARGET  THEN
    INSERT([ID],[OID]) VALUES(Source.[ID],Source.[OID])
WHEN NOT MATCHED BY SOURCE THEN 
    DELETE;


IF NOT EXISTS(
    SELECT * FROM sys.indexes ix Where ix.name = 'PK_A' AND ix.object_id = OBJECT_ID('[StagingDB].[staging].[A]')
)
BEGIN
        ALTER TABLE [StagingDB].[staging].[A] add constraint PK_A primary key CLUSTERED ([ID])
END

これはほとんどのテーブルで非常に迅速に実行されます。残念ながら、はるかに多くの行を持つ2つのテーブルがあり、さらにこれら2つのテーブルのさらに多くの列がレポート作成のプロセスで後で使用されているため、はるかに多くのデータを転送する必要があります。

この種のマージコマンドを71のテーブルに対して1分未満で実行できますが、問題のある2つのMERGEステートメントはそれぞれ約2時間実行されます。その理由はまだわかりませんでした。

日付の量だけでなく、表の違いを比較する方法も疑っています。 UPDATEステートメントを実行する必要があるかどうかを確認するには、Mergeステートメントに含まれている各列を比較します。したがって、10個の列がある場合、MERGEのUPDATE条件は次のようになります。

WHEN MATCHED AND (Target.[OID] <> Source.[OID] OR Target.[OID1] <> Source.[OID2]  OR Target.[Text1] <> Source.[Text1] OR Target.[varcharlong1] <> Source.[varcharlong1] )  etc...

したがって、この種の更新条件チェックにはすべての列が含まれ、インデックスがないことがわかります。ステージングテーブルは常にuniqueidentifierにクラスター化された主キーを持ち、(これまでのところ)追加のインデックスはありません。

私の質問は、MERGEステートメントはほとんどのテーブルで非常に高速かつ効率的に実行されていますが、この手順は、複数の列を含む2つの大きなテーブルには適さないようです。この場合、行を更新する必要があることを確認するためにOR句を使用してMERGEステートメントを正しく使用していますか?列のサブセットの差分(変更)をステージングテーブルにすばやく取得するより良い方法はありますか?すべてのテーブルにROWVERSIONがあります。これを使用して、なんらかの方法で変更された行を特定できますか?

2
Magier

マージを使用しないでください。大きなテーブルでは常にパフォーマンスの問題が発生します。重要なWordマージがSQL Serverに導入されて以来、これは進行中の問題です。以下のMicrosoft Connectおよび他の1つのサイトを参照してください。

https://connect.Microsoft.com/SQLServer/feedback/details/635778/not-matched-and-matched-parts-of-a-sql-merge-statement-are-not-optimized

http://www.sqlservercentral.com/Forums/Topic1465931-391-1.aspx

さて、コードを見ると、すべてを1つのパスで実行しようとしているように見えます(多くの場合、これは良いアイデアです)。この場合、WHEN NOT MATCHED述語を使用しています。これには、完全外部結合が一致する行と一致しない行を1つのパスに含める必要があります。個々のDELETEおよびUPDATEステートメントはこの問題の影響を受けないため、実際にはパフォーマンスが向上します。

これがGianlucaに役立つことを願っています

2
Duane Lawrence