web-dev-qa-db-ja.com

MERGEのベストプラクティスの明確化

MERGEステートメントのパフォーマンスの最適化 」ページから、MSDNで何かについて説明を求めたかっただけです。

多くの異なるデータベースからレコードを取り込んでデータを保管するデータウェアハウスを使用しています。私のウェアハウスデータベースのすべてのテーブルは、基本的に同じパターンに従います。

CREATE TABLE Foo (
    database_guid UNIQUEIDENTIFIER
    ,FooPk BIGINT
    ,Bar NVARCHAR(20)
    ,Qix NCHAR(10)
    ,CONSTRAINT [PK_Foo] PRIMARY KEY CLUSTERED (
        database_guid ASC
        ,FooPk ASC
        )
    )
GO

CREATE PROCEDURE [iv].[LoadSomeTable] 
    @databaseGUID UNIQUEIDENTIFIER
AS
BEGIN
    SET NOCOUNT ON

    MERGE Foo
    USING #FooStaging AS Source
    ON Foo.FooPk = Source.FooPk AND Foo.database_guid = @databaseGUID
    WHEN MATCHED THEN
        UPDATE SET Bar = Source.Bar
                  ,Qix = Source.Qix
    WHEN NOT MATCHED THEN
        INSERT (database_guid, FooPk, Bar, Qix)
            VALUES (@databaseGUID, FooPk, Bar, Qix);
END
GO

CREATE TABLE #FooStaging (
    FooPk BIGINT
    ,Bar NVARCHAR(20)
    ,Qix NCHAR(10)
    )

--Data gets loaded in to #FooStaging from a C# call to SqlBulkCopy then calls iv.LoadSomeTable

私が今心配しているのは、MSDNページからこのステートメントを読んだだけです。

ON <merge_search_condition>句には、ソーステーブルとターゲットテーブルのデータの一致基準を決定する検索条件のみを指定します。つまり、ソース表の対応する列と比較されるターゲット表の列のみを指定します。定数などの他の値との比較は含めないでください。

それを読んだ後、私は自分のクエリを間違って行ったと思っており、私のマージステートメントは

MERGE Foo
USING #FooStaging AS Source
ON Foo.FooPk = Source.FooPk
WHEN MATCHED AND Foo.database_guid = @databaseGUID THEN
    UPDATE SET Bar = Source.Bar
              ,Qix = Source.Qix
WHEN NOT MATCHED THEN
    INSERT (database_guid, FooPk, Bar, Qix)
        VALUES (@databaseGUID, FooPk, Bar, Qix);

しかし、database_guidフィールドは主キーの一部であるため、onに含めないでください。 WHEN MATCHEDにそれがあり、FooPk1のデータベースを1つアップロードした場合、FooPkと別の@databaseGUIDの2番目のデータベースをアップロードします。NOT MATCHEDは発火するかどうか(テストしただけではありません)。

MERGEを使用する正しい方法はどちらですか?

6

あなたの最善のアプローチは、潜在的なアクションごとに別々のステートメントを使用し、それらを直列化可能なトランザクションに入れることです。面白いセマンティクスや「ベストプラクティス」違反のない、試行された真のステートメントを使用でき、間違った結果のバグや潜在的なインデックスの破損など、この投稿で概説しているすべての問題を回避できます。

9
Aaron Bertrand