2009-11-19 4 views
5

J'ai une table regionkey:Permutation deux lignes DB sans violer les contraintes

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

tout le reste de la base de données est-calée à l'étranger Areaid. Dans cette table il y a un index sur (région, locale) avec une contrainte unique.

Le problème est que j'ai deux enregistrements:

101 MICH DETR 
102 ILLI CHIC 

Et je dois échanger les champs entre eux (région, locale), de sorte que je le vent avec:

101 ILLI CHIC 
102 MICH DETR 

Le approche naïve ne fonctionnera pas parce qu'elle viole l'index unique sur la région et l'environnement local:

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

Comment puis-je faire cela? Y a-t-il une manière atomique de faire le swap? Suggestions?

Répondre

8

Vous ne pouvez pas reporter les contrôles de contrainte dans SQL Server sur plusieurs déclarations (à moins que vous désactivent) donc vous devez éviter le conflit ou le faire dans une déclaration

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

ou, de manière plus conventionnelle (dans une transaction celui-ci)

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; 

Edit: Pourquoi ne pas intervertir les touches pas des valeurs? Il atteint généralement le résultat sain sauf si areaid a une signification

update 
    regionkey 
set 
    areaid = 203 - areaid 
where 
    areaid IN (101, 102); 
+0

Cela peut juste être assez fou pour fonctionner. Pensez-vous qu'il devrait envelopper dans une transaction? – Broam

+0

un seul insert est une seule transaction implicite de toute façon – gbn

+0

Juste un peu plus vite que moi, @gbn ;-) – karlgrz

1

Le premier pari est de faire trois mises à jour. Mettez à jour le premier enregistrement à un ensemble de valeurs temporaires, mettez à jour le deuxième enregistrement, puis réajoutez le premier enregistrement aux valeurs souhaitées.

0

Avez-vous essayé le simple fait de l'encapsuler dans une transaction? Je comprends que vous pouvez configurer des contraintes pour lui permettre de ne faire qu'appliquer la contrainte à la fin d'une transaction, mais je ne suis pas sûr que vos contraintes soient configurées de cette façon.

+1

Il n'y a pas de "contrainte de report" dans SQL Server, sauf si vous utilisez explicitement DISABLE qui est une instruction DDL – gbn

+0

Je suis éclairé, merci. – Broam

0

Une suggestion qui peut ne pas être le plus sûr pour les grands ensembles de disques, serait de mettre les deux enregistrements à « » pour les paramètres régionaux région &, puis exécuter deux instructions de mise à jour, un pour chaque enregistrement, comme suit:

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 

comme je l'ai dit, cela est sans doute pas le plus sûr moyen d'aller, mais pour un petit ensemble de données, il devrait être OK. MISE À JOUR: Larry a correctement signalé que la première instruction UPDATE violerait la contrainte UNIQUE. Utilisez ce lieu pour la première mise à jour:

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

De cette façon, chaque région intermédiaire et locale est (ou devrait être) unique.

+0

Même problème - la mise à jour initiale viole UNIQUEness sur la région, les paramètres régionaux. –

+0

Vous avez raison !!! J'ai mis à jour ma réponse pour refléter cela. Bonne prise, @Larry! – karlgrz

Questions connexes