2017-10-31 54 views
0

このストアドプロシージャは、重複キー(フィールド、名前)のpk制約に違反するため、トランザクションが同時に発生すると失敗することがよくあります。私は、 holdlock、 rowlockとbegin/commitトランザクションを試行していますが、同じエラーが発生します。SQL Server MERGEコマンド重複pkエラー

私はinsert文を実行しようとしていますが、同じキー(フィールド、名前)を持つレコードが存在する場合は代わりにそれを更新してください。

パフォーマンスは重要なので、私は一時テーブルソリューションを避けようとしています。

MERGE Fielddata AS TARGET 
USING (VALUES (@Field, @Name, @Value, @File, @Type)) 
    AS SOURCE (Field, Name, Value, File, Type) 
    ON TARGET.Field = @Field AND TARGET.Name = @Name 
WHEN MATCHED THEN 
    UPDATE 
    SET Value = SOURCE.Value, 
     File = SOURCE.File, 
     Type = SOURCE.Type 
WHEN NOT MATCHED THEN 
    INSERT (Field, Name, Value, File, Type) 
    VALUES (SOURCE.Field, SOURCE.Name, SOURCE.Value, SOURCE.File, SOURCE.Type); 

EDIT:24時間シリアライズ可能/ホールドロックによるテスト。 30分後:エラーなし。

EDIT 2:WITH (SERIALIZABLE)/SET TRANSACTION ISOLATION LEVEL SERIALIZABLEは、重複したキーの問題を効果的に解決し、私たちのケース/シナリオで少しのコストがかかります。

答えて

2

トランザクション分離のレベルを上げる必要があります。

SERIALIZABLE SERIALIZABLE SETトランザクション分離レベルは、次のように指定します:+ステートメントは、データ修正が、まだ他のトランザクションでコミットしていないされてい を読み取ることができません。いいえ 他のトランザクションは、現在のトランザクションが完了するまで、現在の トランザクションによって読み取られたデータを変更できます。 その他 トランザクションは、現在のトランザクションが完了するまで、 にあるキー値を持つ新しい行を挿入することはできません。 範囲ロックは、トランザクション内で実行された各ステートメント の検索条件と一致するキー値の範囲の に配置されます。これにより、 からの他のトランザクションが、現在のトランザクションによって実行された ステートメントのいずれかに該当する行を更新または挿入することがブロックされます。これは、トランザクション内の文のいずれかが が2度目に実行された場合、 は同じ行セットを読み込むことを意味します。範囲ロックは、 トランザクションが完了するまで保持されます。これは、完全な範囲の鍵をロックし、トランザクションが完了するまでロック を保持するため、分離 レベルの中で最も制限的です。並行性は低いので、このオプションは必要な場合にのみ を使用してください。 このオプションは、 トランザクション内のすべてのSELECTステートメントのすべてのテーブルでHOLDLOCKを設定するのと同じ効果を持ちます。

シリアライズ実装

SQL Serverは物理ロックがトランザクションの終了 (したがって非推奨テーブルヒントを取得して保持されるシリアライズ 分離レベルのロッキングの実装を使用するために起こりますHOLDLOCKは とSERIALIZABLEの同義語です)。

の完全なシリアル化可能性の技術的な保証を提供するには、この戦略では十分ではありません。新しいデータまたは変更されたデータがトランザクションによって以前に処理された行の範囲に表示される可能性があります。この 同時実行現象はファントムとして知られており、連続スケジュールでは発生しなかった可能性がある のエフェクトが発生する可能性があります。

ファントム並行性現象に対する保護を確実にするために、シリアライズ可能な分離レベルでSQL Serverによって取り込まロック も 以前に検討インデックスキー値間に現れる から新規または変更された行を防止するために、キー範囲ロックを組み込むことができます。範囲ロック は、シリアライズ可能な分離レベルでは必ずしも取得されません。すべて は、一般的に、SQL Serverは、シリアライズ可能な分離レベルの論理要件を満たすために、十分なロックを取得すると言うことができます( )。 実際、ロック実装では、多くの場合、より多くのデータを取得することがありますが、 よりも厳しいロックは、シリアル化可能性を保証するために本当に必要なものよりも大きいですが、 です。

https://sqlperformance.com/2014/04/t-sql-queries/the-serializable-isolation-level

それがシンプルである場合:挿入

+1

にとってブロックソースだけでなく、範囲だけでなく**必要があります**は、それを過大評価。私の好みは、特定のステートメントに 'HOLDLOCK'ヒントを追加することです。 –

+0

HOLDLOCKは同じ結果を返します –

+0

これは事実上同じでしょうか? '= TARGET.Field ON(VALUES(@Field、@Name、@value、@file、@Type)) 源として(フィールド、名前、値、ファイル、タイプ) を使用して、ターゲット AS Fielddata WITH HOLDLOCKをMERGE @FieldとTARGET.Name = @Name MATCHED THEN UPDATE 設定値= SOURCE.Value、 ファイル= SOURCE.File、 タイプ= SOURCE.Type THEN INSERT(フィールド、名前、値を一致していません、ファイル、タイプ) VALUES(SOURCE.Field、SOURCE.Name、SOURCE.Value、SOURCE.File、SOURCE.Type); ' – LochNess