2016-07-25 55 views
2

我需要強制唯一性列上,但只有當其他列是真實的。例如:Conditinal獨特約束不能正確更新

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

我執行了條件指數獨特性:

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

到目前爲止好,當我試圖改變已設置爲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; 

不過,我正在從節點這些查詢,最好是隻運行一個查詢。

我使用的是Postgres 9.5

+0

嘗試使唯一約束DEFERRABLE INITIALLY DEFERRED。不幸的是不確定語法。 –

+0

實際上似乎它不適用於UNIQUE INDEX,僅限於CONSTRAINT UNIQUE –

+1

使用存儲的函數來更新數據。 '更新測試集的值= false其中property ='a'和value;'會更有效率。順便說一句'更新測試設置值= id = 3其中property ='a';'更簡單。 – Abelisto

回答

3

您的問題是UPDATE statements cannot have an ORDER BY clause in SQL(它可以有一些RDBMS,但不是在PostgreSQL的)。

通常的解決方案是使約束可延遲。但是,您使用的部分唯一索引&索引不能聲明爲可推遲。

改爲使用exclusion constraint:它們是唯一約束條件的推廣&也可以是偏好的。

ALTER TABLE test 
    ADD EXCLUDE (property WITH =) WHERE (value = true) 
    DEFERRABLE INITIALLY DEFERRED;