使用UNIQUE约束Postgres在一个字段中增加一组值

时间:2011-03-19 16:45:22

标签: sql postgresql sql-update

我有一张表格,其中我有一个数字字段 A ,设置为 UNIQUE 。该字段用于指示必须执行某些操作的顺序。我想对所有更大的值进行更新,例如, 3 。例如, 我有

A     
1
2
3
4
5

现在,我想将 1 添加到 A 大于 3 的所有值。所以,结果将是

A     
1
2
3
5
6

问题是,是否可以仅使用一个查询来完成?请记住,我在 A 列上有 UNIQUE 约束。

显然,我试过

UPDATE my_table SET A = A + 1 WHERE A > 3;

但它不起作用,因为我对此字段有约束。

2 个答案:

答案 0 :(得分:10)

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;

只要这些语句出现在单个事务中,这将是完全安全的 - 其他会话永远不会看到临时行。缺点是你要两次写行。

请注意,这只是一个唯一索引,而不是约束,但实际上它并不重要。

答案 1 :(得分:0)

您可以通过循环执行此操作。我不喜欢这个解决方案,但它确实有效:

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(至少不是我记忆中的最佳选择)。