6

私はテーブルを持っていますregionkey:

areaid  -- primary key, int
region  -- char(4)
locale  -- char(4)

データベースの残りの部分全体は、areaid への外部キーです。このテーブルには、一意の制約を持つ (地域、ロケール) のインデックスがあります。

問題は、2 つのレコードがあることです。

101   MICH   DETR
102   ILLI   CHIC

そして、それらの間で (地域、ロケール) フィールドを交換する必要があるため、次のようになります。

101   ILLI   CHIC
102   MICH   DETR

地域とロケールの一意のインデックスに違反するため、素朴なアプローチは機能しません。

update regionkey
     set region='ILLI', locale='CHIC' where areaid = 101; -- FAILS
update regionkey
     set region='MICH', locale='DETR' where areaid = 102;

これどうやってするの?スワップを行うアトミックな方法はありますか? 提案?

4

4 に答える 4

9

SQL Serverの制約チェックを複数のステートメントで延期することはできないため(DISABLEを使用しない限り)、競合を回避するか、1つのステートメントで実行する必要があります。

update
    regionkey 
set
    region= CASE areaid WHEN 101 THEN 'ILLI' ELSE 'MICH' END, 
    locale= CASE areaid WHEN 101 THEN 'CHIC' ELSE 'DETR' END
where
    areaid IN (101, 102); 

または、より一般的に(トランザクションではこれ)

update regionkey 
     set region='AAAA', locale='BBBB' where areaid = 101;
update regionkey 
     set region='MICH', locale='DETR' where areaid = 102;
update regionkey 
     set region='ILLI', locale='CHIC' where areaid = 101;

編集:なぜ値ではなくキーを交換しないのですか?areaidに何らかの意味がない限り、通常は正常な結果が得られます。

update
    regionkey 
set
    areaid = 203 - areaid 
where
    areaid IN (101, 102); 
于 2009-11-19T19:53:25.793 に答える
1

最善の策は、3つの更新を行うことです。最初のレコードを一時的な値のセットに更新し、2番目のレコードを更新してから、最初のレコードを必要な値に再更新します。

于 2009-11-19T19:53:08.753 に答える
0

トランザクションでラップするという単純な行為を試しましたか?

トランザクションの終了時にのみ制約を適用できるように制約を設定できることは理解していますが、制約がそのように設定されているかどうかはわかりません。

于 2009-11-19T19:53:43.123 に答える
0

大規模なレコード セットに対して最も安全ではない可能性がある 1 つの提案は、地域とロケールの両方で両方のレコードを ' ' に設定し、次のように各レコードに対して 1 つずつ、2 つの更新ステートメントを実行することです。

UPDATE
    regionkey
SET
   region = '    ',
   locale = '    '
WHERE
    areaid in (101,102)

UPDATE
    regionkey
SET
    region = 'ILLI',
    locale = 'CHIC'
WHERE
    areaid = 101

UPDATE
    regionkey
SET
    region = 'MICH',
    locale = 'DETR'
WHERE
    areaid = 102

前述したように、これはおそらく最も安全な方法ではありませんが、小さなデータ セットの場合は問題ありません。

UPDATE: Larry は、最初の UPDATE ステートメントが UNIQUE 制約に違反することを正しく指摘しました。最初の UPDATE には代わりにこれを使用します。

UPDATE
    regionkey
SET
    region = areaid,
    locale = areaid
WHERE
    areaid in (101,102)

このように、各中間地域とロケールは一意です (または一意である必要があります)。

于 2009-11-19T19:55:37.957 に答える