更新多行时出错 "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.
The referenced columns must be the columns of a non-deferrable unique
or primary key constraint in the referenced table.
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
我在 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 anON CONFLICT DO UPDATE
clause.
The referenced columns must be the columns of a non-deferrable unique or primary key constraint in the referenced table.
When a
UNIQUE
orPRIMARY 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 asDEFERRABLE
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