web-dev-qa-db-ja.com

ターゲットが一致しないときに更新ソースでMERGEを実行する方法?

一時テーブルから新しい値を取得し、それらを実際のテーブルにマージして、挿入された行のIDと、その値と一致する既存の行を取得するストアドプロシージャを記述しようとしています。

さて、私が見ているところから見ると、MERGE関数は、間違っている場合を除いて、私がやろうとしていることをサポートしているようには見えません(完全にありそうです、私はSQLの人ではありません-.NET)。とにかく、私は今夜本当にMERGEについて本当に学びました、そして最初は私が使うべきであるように思われました、しかしそれは私が必要とする機能を持っていないようです。残りのストアドプロシージャに必要です。私の主な焦点は、これが機能するかどうかを確認することでしたが、他の問題は、MERGEを介して実行されるストアドプロシージャへの潜在的な同時呼び出しをコードが処理できることを確認する必要があることです。今はそこに閉じ込められていないことはわかっていますが、この時点で機能させるためだけにしようとしていました。だから、私が持っているもの....

セットアップ

DECLARE @Metadata TABLE
(
    MetadataId INT PRIMARY KEY IDENTITY(1,1),
    MetadataTypeId INT,
    MetadataTypeValueId INT
)

INSERT INTO @Metadata 
VALUES
(1,23),(2,32),(2,33),(2,43),(1,24),(3,33),(1,20)

--Original Table Data
SELECT * FROM @Metadata

DECLARE @MergeMetadata TABLE
(
    MetadataId INT,
    MetadataTypeId INT,
    MetadataTypeValueId INT
)

INSERT INTO @MergeMetadata (MetadataTypeId, MetadataTypeValueId)
VALUES
(2,32),(2,35),(3,34),(4,1),(1,23)

--Metadata that needs added and/or ID'd if exists
SELECT * FROM @MergeMetadata

DECLARE @FinalMetadata TABLE
(
    MetadataId INT,
    MetadataTypeId INT,
    MetadataTypeValueId INT
)

INSERT INTO @FinalMetadata (MetadataId, MetadataTypeId, MetadataTypeValueId)
VALUES
(2,2,32),(8,2,35),(9,3,34),(10,4,1),(1,1,23)

--This is the same Type/TypeValue Id's from the table above
--However, they've been ID'd with existing IDs from the original table
--Or inserted and ID'd if they didn't exist
--Order doesn't matter for the result, I just need the IDs
SELECT * FROM @FinalMetadata

試行1

最初に、一致したときに結果テーブルに挿入し、出力を使用して新しい値を取得することにより、マージによって値を取得しようとしました。

--Try to Insert the existing target when matched into the final table
MERGE @Metadata AS T
    USING @MergeMetadata AS S
        ON (S.MetadataTypeId = T.MetadataTypeId 
        AND S.MetadataTypeValueId = T.MetadataTypeValueId)
    WHEN MATCHED
        THEN --CANT INSERT WHEN MATCHED
            INSERT INTO (T.MetadataId, S.MetadataTypeId, S.MetadataTypeValueId) INTO @FinalMetadata
    WHEN NOT MATCHED BY TARGET
        THEN 
            INSERT (MetadataTypeId, MetadataTypeValueId) 
            VALUES (S.MetadataTypeId, S.MetadataTypeValueId)
    OUTPUT inserted.MetadataId, 
           inserted.MetadataTypeId, 
           inserted.MetadataValueTypeId,
           INTO @FinalMetadata
        ;

試行2

次に、OUTPUTを使用して元のIDを取得しようとしました-UPDATEDのプレースホルダーがないことに気付きませんでした:(

--Try to get the original IDs by OUTPUT
MERGE @Metadata AS T
    USING @MergeMetadata AS S
        ON (S.MetadataTypeId = T.MetadataTypeId 
        AND S.MetadataTypeValueId = T.MetadataTypeValueId)
    WHEN MATCHED
        THEN 
            UPDATE SET (T.MetadataId = T.MetadataId)
    WHEN NOT MATCHED BY TARGET
        THEN 
            INSERT (MetadataTypeId, MetadataTypeValueId) 
            VALUES (S.MetadataTypeId, S.MetadataTypeValueId)
    OUTPUT inserted.MetadataId, 
           updated.MetadataId, --updated isn't a key for OUTPUT 
           inserted.MetadataValueTypeId,
           INTO @FinalMetadata
        ;

試行3

最後に、攻撃計画を元に戻そうとしましたが、ソースの挿入を実行できませんでした

--Try to reverse logic and insert into Source when not matched
MERGE @MergeMetadata T
    USING @Metadata S
        ON (S.MetadataTypeId = T.MetadataTypeId 
        AND S.MetadataTypeValueId = T.MetadataTypeValueId)
    WHEN MATCHED
        THEN UPDATE SET T.MetadataId = S.MetadataId
    WHEN NOT MATCHED BY SOURCE
        THEN --can't insert in NOT MATCHED BY SOURCE
            INSERT (MetadataTypeId, MetadataTypeValueId) 
            VALUES (T.MetadataTypeId, T.MetadataTypeValueId) 
    ; 

長すぎて申し訳ありませんが、私がこれにいくつかの考えと試みをしたことを示したかっただけです。この時点で、代わりにSELECT/IF NOT EXISTS/BEGIN INSERTクラシックロジックを記述する必要がありますか?私がMERGEを読んでいるすべてから、それはこの状況に最適に聞こえましたが、本当に必要なものを私に与えたくないようです。

他のロジックのルートに進む必要がある場合、同時実行で同じDOES NOT EXIST/INSERTが同時に実行されないように、並行性を処理するいくつかの推奨コードのスニペットを提供できますか?

7
tostringtheory

更新はMERGEによって以下を提供することにより追跡されます。

  • $ Action値[〜#〜] update [〜#〜];
  • メモリテーブルの古い値削除済み;そして
  • メモリテーブルの新しい値挿入

したがって、一致が見つかった場合と、ターゲットによる一致がない場合の両方で、ID列の値は挿入メモリテーブルで使用できます。

3
Pieter Geerkens

MERGEステートメントを使用して1泊の経験で多くのことを行いました!

OUTPUT句のDELETED列のプレフィックスには、MERGE UPDATE SETステートメントで更新された値が含まれている必要があります。これは、試行2で順調に進んでいると思います。

2
user2891288