MPę*_*ski 8 sql postgresql sql-update
我有一个表,其中我有一个数字字段A,它被设置为UNIQUE.该字段用于指示必须执行某些操作的顺序.我想对所有更大的值进行更新,例如,超过3.例如,我有
A
1
2
3
4
5
Run Code Online (Sandbox Code Playgroud)
现在,我想为A大于3的所有值添加1.所以,结果就是
A
1
2
3
5
6
Run Code Online (Sandbox Code Playgroud)
问题是,是否可以只使用一个查询来完成?请记住,我对A列有一个UNIQUE约束.
显然,我试过了
UPDATE my_table SET A = A + 1 WHERE A > 3;
Run Code Online (Sandbox Code Playgroud)
但它不起作用,因为我对这个领域有约束.
int*_*tgr 10
PostgreSQL 9.0增加了可延迟的唯一约束,这正是您似乎需要的功能.这样,在提交时而不是更新时检查唯一性.
使用DEFERRABLE关键字创建UNIQUE约束:
ALTER TABLE foo ADD CONSTRAINT foo_uniq (foo_id) DEFERRABLE;
Run Code Online (Sandbox Code Playgroud)
稍后,在运行UPDATE语句之前,您将在同一事务中运行:
SET CONSTRAINTS foo_uniq DEFERRED;
Run Code Online (Sandbox Code Playgroud)
或者,您可以使用INITIALLY DEFERRED唯一约束本身的关键字创建约束- 因此您不必运行SET CONSTRAINTS- 但这可能会影响其他查询的性能,而这些查询不需要推迟约束.
如果您只想使用唯一约束来保证唯一性 - 而不是作为外键的目标 - 那么这种解决方法可能会有所帮助:
首先,将一个布尔列添加is_temporary到临时区分更新和未更新行的表中:
CREATE TABLE foo (value int not null, is_temporary bool not null default false);
Run Code Online (Sandbox Code Playgroud)
接下来创建一个仅影响is_temporary = false的行的部分唯一索引:
CREATE UNIQUE INDEX ON foo (value) WHERE is_temporary=false;
Run Code Online (Sandbox Code Playgroud)
现在,每次都进行您描述的更新,您可以分两步运行它们:
UPDATE foo SET is_temporary=true, value=value+1 WHERE value>3;
UPDATE foo SET is_temporary=false WHERE is_temporary=true;
Run Code Online (Sandbox Code Playgroud)
只要这些语句出现在单个事务中,这将是完全安全的 - 其他会话永远不会看到临时行.缺点是你要写两次行.
请注意,这只是一个独特的索引,而不是约束,但实际上它并不重要.
您可以使用一个简单的技巧在 2 个查询中完成此操作:
首先,用 +1 更新您的列,但添加 ax(-1) 因子:
update my_table set A=(A+1)*-1 where A > 3.
Run Code Online (Sandbox Code Playgroud)
您将从 4,5,6 切换到 -5,-6,-7
二、转换回恢复正值的操作:
update my_table set A=(A)*-1 where A < 0.
Run Code Online (Sandbox Code Playgroud)
您将拥有:5,6,7