なぜこれが機能しないのですか?
SQL Serverのデフォルトの動作は、共有ロックが不要になったらすぐに解放することだと思います。サブクエリを実行すると、テーブルに短期間の共有(S)ロックが発生し、サブクエリが完了するとすぐに解放されます。
この時点で、並行トランザクションが、確認したばかりの行が存在しなかったことを挿入することを妨げるものは何もありません。
制約違反による例外の可能性がないようにするには、どのような変更を加える必要がありますか?
HOLDLOCK
を追加する サブクエリへのヒントは、トランザクションが完了するまでロックを保持するようにSQLServerに指示します。 (あなたの場合、これは暗黙のトランザクションです。)HOLDLOCK
ヒントはSERIALIZABLE
と同等です ヒント。これ自体は、「その他のアプローチ」のリストで参照するシリアル化可能なトランザクション分離レベルに相当します。
HOLDLOCK
Sロックを保持し、並行トランザクションが保護している行を挿入するのを防ぐには、ヒントだけで十分です。ただし、同じ頻度で発生するデッドロックに置き換えられた一意のキー違反エラーが見つかる可能性があります。
テーブルにSロックのみを保持している場合は、同じ行を挿入する2つの同時試行間の競合を検討し、ロックステップに進みます。どちらもテーブルのSロックの取得に成功しますが、どちらも排他の取得に成功することはできません。 (X)挿入を実行するために必要なロック。
幸い、この正確なシナリオには、更新(U)ロックと呼ばれる別のロックタイプがあります。 UロックはSロックと同じですが、次の違いがあります。同じリソースで複数のSロックを同時に保持できますが、一度に保持できるUロックは1つだけです。 (別の言い方をすれば、Sロックは互いに互換性がありますが(つまり、競合することなく共存できます)、Uロックは互いに互換性がありませんが、Sロックと共存できます。さらにスペクトルに沿って、排他的(X)ロックは互換性がありません。 SまたはUロックと互換性があります)
UPDLOCK
を使用して、サブクエリの暗黙的なSロックをUロックにアップグレードできます。 ヒント。
テーブルに同じ行を挿入する2つの同時試行は、最初のselectステートメントでシリアル化されます。これは、同時挿入試行からの別のUロックと互換性のないUロックを取得(および保持)するためです。
NULL値
FieldCがNULL値を許可するという事実から、別の問題が発生する可能性があります。
ANSI_NULLS
の場合 がオン(デフォルト)の場合、等価性チェックFieldC=NULL
FieldCがNULLの場合でもfalseを返します(IS NULL
を使用する必要があります ANSI_NULLS
のときにnullをチェックする演算子 オンです)。 FieldCはNULL可能であるため、NULL値を挿入するときに重複チェックは機能しません。
nullを正しく処理するには、EXISTSサブクエリを変更してIS NULL
を使用する必要があります。 =
ではなく演算子 NULLの値が挿入されているとき。 (または、関連するすべての列でNULLを許可しないようにテーブルを変更できます。)
SQLServerBooksオンラインリファレンス
- ロックのヒント
- ロック互換性マトリックス
- ANSI_NULLS