使用一个查询递增具有唯一约束的字段中的一组值,Postgres
Posted
技术标签:
【中文标题】使用一个查询递增具有唯一约束的字段中的一组值,Postgres【英文标题】:Incrementing with one query a set of values in a field with UNIQUE constraint, Postgres 【发布时间】:2011-03-19 16:45:21 【问题描述】:我有一个表格,其中有一个数字字段 A,它被设置为 UNIQUE。该字段用于指示必须执行某些操作的顺序。我想对所有大于 3 的值进行 UPDATE。例如, 我有
A
1
2
3
4
5
现在,我想将 1 添加到所有大于 3 的 A 值。所以,结果是
A
1
2
3
5
6
问题是,是否可以只使用一个查询来完成?请记住,我在 A 列上有一个 UNIQUE 约束。
显然,我试过了
UPDATE my_table SET A = A + 1 WHERE A > 3;
但它不起作用,因为我对该字段有限制。
【问题讨论】:
以前问过这里,***.com/questions/674644/database-update-order 总而言之,你能不能把约束去掉,完成后再加回来? 这里不能选择删除约束。如果我想这样做,我可能会使用一个简单的过程并以指定的顺序更新值,这样我就可以省略重复键问题。 我已经根据部分唯一索引,用一个新想法更新了我的答案。这能解决您的问题吗? 当我能够检查它如何解决我的问题时,我会回答它。那可能是明天。 【参考方案1】:PostgreSQL 9.0 及更高版本
PostgreSQL 9.0 添加了deferrable unique constraints,这正是您似乎需要的功能。这样,在提交时而不是更新时检查唯一性。
使用 DEFERRABLE 关键字创建 UNIQUE 约束:
ALTER TABLE foo ADD CONSTRAINT foo_uniq (foo_id) DEFERRABLE;
稍后,在运行 UPDATE 语句之前,您在同一个事务中运行:
SET CONSTRAINTS foo_uniq DEFERRED;
或者,您可以在唯一约束本身上使用INITIALLY DEFERRED
关键字创建约束——因此您不必运行SET CONSTRAINTS
——但这可能会影响其他不需要的查询的性能推迟约束。
PostgreSQL 8.4 及更早版本
如果您只想使用唯一性约束来保证唯一性——而不是作为外键的目标——那么这种解决方法可能会有所帮助:
首先,在表中添加一个布尔列,例如is_temporary
,用于临时区分更新和未更新的行:
CREATE TABLE foo (value int not null, is_temporary bool not null default false);
接下来创建一个部分唯一索引,它只影响 is_temporary=false 的行:
CREATE UNIQUE INDEX ON foo (value) WHERE is_temporary=false;
现在,每次进行您描述的更新时,您都分两步运行它们:
UPDATE foo SET is_temporary=true, value=value+1 WHERE value>3;
UPDATE foo SET is_temporary=false WHERE is_temporary=true;
只要这些语句发生在单个事务中,这将是完全安全的——其他会话永远不会看到临时行。缺点是你会写两次行。
请注意,这只是一个唯一索引,而不是约束,但实际上它应该无关紧要。
【讨论】:
我应该补充说我使用的是 Postgres 8.4。所以,这不适用。不过,给你一点。 在 8.4 中,您还可以通过向唯一约束添加另一列来解决此问题——它区分未更新和已更新的行。不幸的是,这也意味着它不再真正保证唯一性。如果这对您不起作用,除了放弃约束之外,9.0 是唯一的选择。 是的,我想过再增加一列,但这几乎和完全没有约束一样。现在,我正在尝试用 recursive with 组成一些解决方案。 我根据部分唯一索引更新了我的答案。 对于这么晚的回复,我深表歉意,但我有一段时间无法访问我的数据库。但是,您的解决方案完美运行。因此,我接受你的回答。再次感谢您的时间和帮助。【参考方案2】:您可以通过一个简单的技巧在 2 个查询中完成:
首先,使用 +1 更新您的列,但添加带有 x(-1) 因子的 a:
update my_table set A=(A+1)*-1 where A > 3.
您将从 4,5,6 切换到 -5,-6,-7
二、转回操作恢复正数:
update my_table set A=(A)*-1 where A < 0.
你将拥有:5,6,7
【讨论】:
【参考方案3】:您可以使用循环来执行此操作。我不喜欢这个解决方案,但它确实有效:
CREATE TABLE update_unique (id INT NOT NULL);
CREATE UNIQUE INDEX ux_id ON update_unique (id);
INSERT INTO update_unique(id) SELECT a FROM generate_series(1,100) AS foo(a);
DO $$
DECLARE v INT;
BEGIN
FOR v IN SELECT id FROM update_unique WHERE id > 3 ORDER BY id DESC
LOOP
UPDATE update_unique SET id = id + 1 WHERE id = v;
END LOOP;
END;
$$ LANGUAGE 'plpgsql';
在 PostgreSQL 8.4 中,您可能必须创建一个函数来执行此操作,因为您不能使用 DO 在提示符下任意运行 PL/PGSQL(至少在我的记忆中不是这样)。
【讨论】:
这将解决更新问题,但问题是是否可以使用 one query 完成,即单独使用 SQL(无 PL/PGSQL)。以上是关于使用一个查询递增具有唯一约束的字段中的一组值,Postgres的主要内容,如果未能解决你的问题,请参考以下文章