更新期间意外违反唯一约束

ora*_*nge 4 postgresql subquery update unique-constraint

foo在具有两列(id和)的表中seq,我想为seq具有任意 . 的所有记录添加+1 seq > 4738。计划是在seq=4739所有seq > 4738记录移动+1 后立即插入一条新记录。

这是桌子。

CREATE TABLE foo
(
  id uuid NOT NULL,
  seq integer NOT NULL,
  CONSTRAINT seq_key UNIQUE (seq)
)

CREATE UNIQUE INDEX idx_id
  ON foo
  USING btree
  (id);

CREATE UNIQUE INDEX idx_seq
  ON foo
  USING btree
  (seq);
Run Code Online (Sandbox Code Playgroud)

我尝试通过以下查询实现 +1 转变。请注意,我使用子查询尝试> 4738按降序更新记录(即假设 max seq=10000,则首先更新最后一条记录(10000->10001),然后更新倒数第二条记录(seq=10000此时不存在,seq=9999-> seq=10000(没有违反约束),然后是 9998 -> 9999,...以避免在任何时候发生唯一的约束违反。但是,这假设更新查询是顺序执行的,而这似乎并不是发生的情况。

跑步时

UPDATE foo SET seq=anon_1.new_seq FROM (
  SELECT foo.id AS id, foo.seq + 1 AS new_seq
  FROM foo
  WHERE foo.seq > 4738 ORDER BY foo.seq DESC
) AS anon_1 WHERE foo.id = anon_1.id
Run Code Online (Sandbox Code Playgroud)

我收到以下错误。

重复的键值违反了唯一约束“seq_key”详细信息:键(seq)=(7334)已存在。

显然,这是出乎意料的(因为约束在 之前已得到满足UPDATE)。有什么我可以尝试解决问题的吗(不同的索引类型,只有约束,只有索引)?我注意到这个错误很大程度上取决于更新记录的数量。如果更新的记录较少,那么这个问题似乎不会发生(这可能暗示当有太多记录需要更新时会进行一些并行执行,这可能会交错并导致某种中间状态,其中约束/索引不是唯一的)更多的)。一些想法和想法将不胜感激。

小智 7

您需要创建可延迟约束而不是索引。

CREATE TABLE foo
(
  id uuid NOT NULL primary key
  seq integer NOT NULL,
  CONSTRAINT seq_key UNIQUE (seq)
    deferrable initially immediate --<< this
);
Run Code Online (Sandbox Code Playgroud)

通过将约束声明为可延迟,它将在 UPDATE 语句末尾(更准确地说:在事务末尾)而不是逐行检查。