sql >> データベース >  >> RDS >> Sqlserver

SQLServer2005のアトミックUPSERT

    INSERT INTO <table>
    SELECT <natural keys>, <other stuff...>
    FROM <table>
    WHERE NOT EXISTS
       -- race condition risk here?
       ( SELECT 1 FROM <table> WHERE <natural keys> )
    
    UPDATE ...
    WHERE <natural keys>
    
    • 最初のINSERTには競合状態があります。キーは内部クエリSELECT中に存在しない可能性がありますが、INSERT時に存在するため、キー違反が発生します。
    • INSERTとUPDATEの間には競合状態があります。キーは、INSERTの内部クエリでチェックインされたときに存在する可能性がありますが、UPDATEが実行されるまでになくなります。

    2番目の競合状態では、キーは並行スレッドによってとにかく削除されたと主張する可能性があるため、更新が失われたわけではありません。

    最適な解決策は通常、最も可能性の高いケースを試し、失敗した場合はエラーを処理することです(もちろん、トランザクション内で):

    • キーが欠落している可能性がある場合は、常に最初に挿入してください。一意の制約違反、更新へのフォールバックを処理します。
    • キーが存在する可能性が高い場合は、常に最初に更新してください。行が見つからなかった場合は挿入します。一意の制約違反の可能性を処理し、更新へのフォールバック。

    正確性に加えて、このパターンは速度にも最適です。偽のロックアップを実行するよりも、例外を挿入して処理する方が効率的です。ロックアップは論理的なページ読み取り(物理的なページ読み取りを意味する場合があります)を意味し、IO(論理的であっても)はSEHよりもコストがかかります。

    更新 @ピーター

    単一のステートメントが「アトミック」ではないのはなぜですか?ささいなテーブルがあるとしましょう:

    create table Test (id int primary key);
    

    ここで、この1つのステートメントを2つのスレッドからループで実行すると、競合状態が存在しない可能性があるため、「アトミック」になります。

      insert into Test (id)
        select top (1) id
        from Numbers n
        where not exists (select id from Test where id = n.id); 
    

    しかし、ほんの数秒で、主キー違反が発生します:

    メッセージ2627、レベル14、状態1、行4
    PRIMARYKEY制約の違反'PK__Test__24927208'。オブジェクト'dbo.Test'に重複するキーを挿入できません。

    何故ですか? SQLクエリプランがDELETE ... FROM ... JOINで「正しいこと」を実行するという点で正しいです。 、WITH cte AS (SELECT...FROM ) DELETE FROM cte そして他の多くの場合。ただし、これらの場合には決定的な違いがあります。「サブクエリ」はターゲットを指します 更新の または削除 手術。このような場合、クエリプランは実際に適切なロックを使用します。実際、この動作は、テーブルをキューとして使用してキューを実装する場合など、特定の場合に重要です。

    しかし、元の質問と私の例では、サブクエリは、特別なロック保護を必要とする特別な「更新のスキャン」タイプのクエリとしてではなく、クエリのサブクエリとしてクエリオプティマイザによって表示されます。その結果、サブクエリルックアップの実行は同時オブザーバーによる個別の操作として監視できます 、したがって、ステートメントの「アトミック」動作を壊します。特別な予防策を講じない限り、複数のスレッドが同じ値を挿入しようとする可能性があります。両方とも、チェック済みであり、値がまだ存在していないことを確信しています。成功できるのは1つだけで、もう1つはPK違反になります。 QED。



    1. 2つの日付間の営業時間を計算します

    2. Pythonでのデータベース接続プールの最良の解決策は何ですか?

    3. リレーショナルデータベースとは何ですか?

    4. SQLiteにGLOB句が必要なのはなぜですか?