Pet*_*ryl 17 postgresql transaction unique-constraint
可以在 PostgreSQL 中创建原子事务吗?
考虑我有这些行的表类别:
id|name
--|---------
1 |'tablets'
2 |'phones'
Run Code Online (Sandbox Code Playgroud)
并且列名具有唯一约束。
如果我尝试:
BEGIN;
update "category" set name = 'phones' where id = 1;
update "category" set name = 'tablets' where id = 2;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
我越来越:
ERROR: duplicate key value violates unique constraint "category_name_key"
DETAIL: Key (name)=(tablets) already exists.
Run Code Online (Sandbox Code Playgroud)
Erw*_*ter 25
除了@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;
Run Code Online (Sandbox Code Playgroud)
在单个语句中删除并添加约束,这样任何人都没有时间窗口偷偷插入违规行。对于大表,以某种方式保存底层唯一索引很诱人,因为删除和重新创建它的成本很高。唉,这似乎无法使用标准工具(如果您有解决方案,请告诉我们!):
对于使约束可延迟的单个语句就足够了:
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;
Run Code Online (Sandbox Code Playgroud)
与CTE的查询也是一个单一的语句:
WITH x AS (
UPDATE category SET name = 'phones' WHERE id = 1
)
UPDATE category SET name = 'tablets' WHERE id = 2;
Run Code Online (Sandbox Code Playgroud)
但是,对于包含多个语句的代码,您(另外)需要实际推迟约束 - 或者将其定义为“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;
Run Code Online (Sandbox Code Playgroud)
要知道一个的限制与连接FOREIGN KEY
的限制,虽然。根据文档:
被引用的列必须是被引用表中不可延迟的唯一或主键约束的列。
所以你不能同时拥有两者。
Cra*_*ger 13
据我了解,您的问题是在每个语句之后检查约束,但您希望在事务结束时检查它,因此它将前状态与后状态进行比较,忽略中间状态。
如果是这样,则可以使用延迟约束。
请参阅SET CONSTRAINTS
和 中记录的DEFERRABLE
约束CREATE TABLE
。
请注意,延迟约束是有成本的——系统必须保留一个它们的列表以在提交时检查,因此它们不适用于进行大量更改的事务。它们的检查速度也较慢。
所以我想你可能想要:
ALTER TABLE mytable ALTER CONSTRAINT category_name_key DEFERRABLE;
Run Code Online (Sandbox Code Playgroud)
请注意,ALTER TABLE
将约束设置为DEFERRABLE
; 您可能不得不改为DROP
重新ADD
约束。
归档时间: |
|
查看次数: |
8218 次 |
最近记录: |