条件唯一约束未正确更新

时间:2016-07-25 08:01:22

标签: postgresql

我需要在列上强制执行唯一性,但仅当其他列为true时才需要。例如:

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.

基本上,如果值为true的行的主键值大于当前行的值,则它可以正常运行。关于如何规避它的任何想法?

我当然能做到:

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

但是,我正在从节点运行这些查询,最好只运行一个查询。

我正在使用Postgres 9.5

1 个答案:

答案 0 :(得分: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;