2016-07-25 13 views
2

他の列が真である場合にのみ、列に一意性を強制する必要があります。コンディション固有の制約が正しく更新されない

create temporary table test(id serial primary key, property character varying(50), value boolean); 
insert into test(property, value) values ('a', false); 
insert into test(property, value) values ('a', true); 
insert into test(property, value) values ('a', false); 

そして、私は条件付きインデックスと一意性を強制:たとえば、私がtrueに設定された値を持つ行を変更しようとすると、

create unique index on test(property) where value = true; 

をこれまでのところは良い、という問題が生じます。

update test set value = new_value from (select id, id=3 as new_value from test where property = 'a')new_test where test.id = new_test.id 

しかし、私はやるときにはしていません:それは私がしなければ動作します

update test set value = new_value from (select id, id=1 as new_value from test where property = 'a')new_test where test.id = new_test.id 

そして私が手:

ERROR: duplicate key value violates unique constraint "test_property_idx" 
DETAIL: Key (property)=(a) already exists. 

********** Error ********** 

ERROR: duplicate key value violates unique constraint "test_property_idx" 
SQL state: 23505 
Detail: Key (property)=(a) already exists. 

真の値を持つ行がある場合は基本的に、それは動作します真理である現在の行よりも大きな値を持つ主キー。どのようにそれを回避するための任意のアイデア?

私が行うことができます。もちろん、

update test set value = false where property='a'; 
update test set value = true where property = 'a' and id = 1; 

はしかし、私は、ノードからこれらのクエリを実行していることだし、それが唯一のクエリを実行することが好ましいです。

私は9.5

+0

一意制約DEFERRABLE INITIALLY DEFERREDを作成してください。不幸にも構文がわからない。 –

+0

UNIQUE INDEXではCONSTRAINT UNIQUEでしか使用できません。 –

+1

ストアドファンクションを使用してデータを更新してください。 'update test set value = falseここでproperty = 'a'とvalue;'はより効率的です。 BTW 'update test set value = id = 3ここでproperty = 'a';はより簡単です。 – Abelisto

答えて

3

あなたの問題は、(それはいくつかのRDBMSではなく、PostgreSQLでは持つことができる)UPDATE statements cannot have an ORDER BY clause in SQLということであるのPostgresを使用しています。

これに対する通常の解決策は、制約を遅延可能にすることです。しかし、部分的なユニークなインデックス&を使用すると、インデックスを遅延可能として宣言することはできません。

代わりにexclusion constraintを使用してください。これらは固有の制約の一般化です&も部分的になります。

関連する問題