除了@Craig提供的内容(并更正其中的一些内容):
有效的Postgres 9.4,UNIQUE
,PRIMARY KEY
和EXCLUDE
约束立即检查各行后定义的时候NOT DEFERRABLE
。这与其他类型的NOT DEFERRABLE
约束(当前仅是REFERENCES
(外键))不同,后者在每个语句之后进行检查。我们在SO的相关问题下解决了所有这些问题:
这是不是足以让一个UNIQUE
(或PRIMARY KEY
或EXCLUDE
)约束是DEFERRABLE
使你的代码提交多条语句的工作。
你也可以不使用ALTER TABLE ... ALTER CONSTRAINT
用于这一目的。每个文档:
ALTER CONSTRAINT
此表单更改了先前创建的约束的属性。当前,只有外键约束可以更改。
大胆强调我的。改用:
ALTER TABLE t
DROP CONSTRAINT category_name_key
, ADD CONSTRAINT category_name_key UNIQUE(name) DEFERRABLE;
将约束拖放到单个语句中,这样任何人都没有时间窗口可以潜入有问题的行中。对于大表,以某种方式保留底层唯一索引将很诱人,因为删除和重新创建它的成本很高。遗憾的是,使用标准工具似乎无法实现(如果您有解决方案,请告诉我们!):
对于单个语句,使约束可延期就足够了:
UPDATE category c
SET name = c_old.name
FROM category c_old
WHERE c.id IN (1,2)
AND c_old.id IN (1,2)
AND c.id <> c_old.id;
与CTE的查询也是一个单一的语句:
WITH x AS (
UPDATE category SET name = 'phones' WHERE id = 1
)
UPDATE category SET name = 'tablets' WHERE id = 2;
但是,对于具有多个语句的代码,您(另外)需要实际推迟约束-或将其定义INITIALLY DEFERRED
为通常比上述方法更昂贵。但是将所有内容打包成一个语句可能并不容易。
BEGIN;
SET CONSTRAINTS category_name_key DEFERRED;
UPDATE category SET name = 'phones' WHERE id = 1;
UPDATE category SET name = 'tablets' WHERE id = 2;
COMMIT;
但是请注意与约束有关的限制FOREIGN KEY
。每个文档:
引用的列必须是引用表中不可延迟的唯一或主键约束的列。
因此,您不能同时拥有两者。