我有一张表格,其中我有一个数字字段 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;
但它不起作用,因为我对此字段有约束。
答案 0 :(得分:10)
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
- 但这可能会影响其他查询的性能不需要推迟约束。
如果您只想使用唯一约束来保证唯一性 - 而不是作为外键的目标 - 那么这种解决方法可能有所帮助:
首先,将一个布尔列(例如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(至少不是我记忆中的最佳选择)。