更新多行时出错 "duplicate key value violates unique constraint"

Error "duplicate key value violates unique constraint" while updating multiple rows

我在 PostgreSQL 和 Oracle 中创建了一个 table 作为

CREATE TABLE temp(
   seqnr smallint NOT NULL,
   defn_id int not null,
   attr_id int not null,
   input CHAR(50) NOT NULL,
   CONSTRAINT pk_id PRIMARY KEY (defn_id, attr_id, seqnr)
);

这个临时 table 的主键作为一个整体 (defn_id,attr_id,seqnr)!

然后我在temp中插入记录table as

INSERT INTO temp(seqnr,defn_id,attr_id,input)
VALUES (1,100,100,'test1');

INSERT INTO temp(seqnr,defn_id,attr_id,input)
VALUES (2,100,100,'test2');

INSERT INTO temp(seqnr,defn_id,attr_id,input)
VALUES (3,100,100,'test3');

INSERT INTO temp(seqnr,defn_id,attr_id,input)
VALUES (4,100,100,'test4');

INSERT INTO temp(seqnr,defn_id,attr_id,input)
VALUES (5,100,100,'test5');

在 Oracle 和 Postgres 中! table 现在包含:

 seqnr |  defn_id | attr_id | input
     1 |      100 |    100  | test1
     2 |      100 |    100  | test2
     3 |      100 |    100  | test3
     4 |      100 |    100  | test4
     5 |      100 |    100  | test5

当我运行命令时:

UPDATE temp SET seqnr=seqnr+1
WHERE defn_id = 100 AND attr_id = 100 AND seqnr >= 1;

对于 ORACLE,它是更新 5 行并且 O/p 是

 seqnr |  defn_id | attr_id | input
     2 |      100 |    100  | test1
     3 |      100 |    100  | test2
     4 |      100 |    100  | test3
     5 |      100 |    100  | test4
     6 |      100 |    100  | test5

但是如果是 PostgreSQL,它会报错!

DETAIL:  Key (defn_id, attr_id, seqnr)=(100, 100, 2) already exists.

为什么会发生这种情况,如何在 Postgres 中复制与 Oracle 相同的结果?
或者如何在 Postgres 中实现相同的结果而没有任何错误?

UNIQUE 立即检查 PRIMARY KEY 约束(对于每一行),除非它们被定义 DEFERRABLE - 这是您的解决方案需求。

ALTER TABLE temp
  DROP CONSTRAINT pk_id
, ADD  CONSTRAINT pk_id PRIMARY KEY (defn_id, attr_id, seqnr) DEFERRABLE
;

那么你的 UPDATE 就可以了。

db<>fiddle here

不过,这需要 成本The manual:

Note that deferrable constraints cannot be used as conflict arbitrators in an INSERT statement that includes an ON CONFLICT DO UPDATE clause.

for FOREIGN KEY constraints

The referenced columns must be the columns of a non-deferrable unique or primary key constraint in the referenced table.

And:

When a UNIQUE or PRIMARY KEY constraint is not deferrable, PostgreSQL checks for uniqueness immediately whenever a row is inserted or modified. The SQL standard says that uniqueness should be enforced only at the end of the statement; this makes a difference when, for example, a single command updates multiple key values. To obtain standard-compliant behavior, declare the constraint as DEFERRABLE but not deferred (i.e., INITIALLY IMMEDIATE). Be aware that this can be significantly slower than immediate uniqueness checking.

参见:

  • Constraint defined DEFERRABLE INITIALLY IMMEDIATE is still DEFERRED?

如果可能的话,我会避免 DEFERRABLE PK。也许您可以解决已证明的问题?这个通常有效:

UPDATE temp t
SET    seqnr = t.seqnr + 1
FROM  (
   SELECT defn_id, attr_id, seqnr
   FROM   temp
   WHERE  defn_id = 100 AND attr_id = 100 AND seqnr >= 1
   ORDER  BY defn_id, attr_id, seqnr DESC
   ) o
WHERE (t.defn_id, t.attr_id, t.seqnr)
    = (o.defn_id, o.attr_id, o.seqnr);

db<>fiddle here

但是没有任何保证,因为在 Postgres 中没有为 UPDATE 指定 ORDER BY

相关:

  • UPDATE with ORDER BY