在不违反约束的情况下交换两个DB行

时间:2009-11-19 19:50:07

标签: sql sql-server tsql atomic

我有一张表regionkey

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

数据库的其余部分都是外键到areaid。在此表中,有一个具有唯一约束的(区域,区域设置)索引。

问题是我有两条记录:

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 个答案:

答案 0 :(得分:8)

您不能在SQL Server中推迟多个语句中的约束检查(除非您禁用),因此您必须避免冲突或在一个语句中执行此操作

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); 

答案 1 :(得分:1)

最好的赌注是进行三次更新。将第一个记录更新为临时值集,更新第二个记录,然后将第一个记录重新更新为您想要的值。

答案 2 :(得分:0)

您是否尝试过在事务中包装它的简单行为?

我知道您可以设置约束以允许它仅在事务结束时强制执行约束,但我不确定您的约束是否以这种方式设置。

答案 3 :(得分:0)

对于大型唱片集而言可能不是最安全的一个建议是将两个记录设置为''对于区域和& locale,然后执行两个update语句,每个记录一个,如下所示:

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

就像我说的那样,这可能不是最安全的方法,但对于一个小数据集应该没问题。

更新:Larry正确地指出第一个UPDATE语句将违反UNIQUE约束。使用此代替第一个更新:

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

这样每个中间区域和区域设置都是(或应该是)唯一的。