一个事务范围内的主键冲突

Primary key collision in scope of one trasaction

我有一个 postgresql 数据库,它严重依赖于来自外部的事件,例如管理员更改/添加某些字段或记录可能会触发其他 tables.

中整体字段结构的更改

然而,问题就在这里,因为有时触发函数更改的字段是主键字段。有一个table,它使用两个外键id作为主键,如下例:

#  | PK id1 | PK id2 |  data  |
0  |   1    |   1    |   ab   |
1  |   1    |   2    |   cd   |
2  |   1    |   3    |   ef   |

然而,在一次交易中(如果我可以这样称呼它,因为实际上它是一个 plpgsql 函数),结构可能会更改为:

#  | PK id1 | PK id2 |  data  |
0  |   1    |   3    |   ab   |
1  |   1    |   2    |   cd   |
2  |   1    |   1    |   ef   |

您可能已经注意到,将第 0 条记录的第二个主键更改为 3,将第 2 条记录的第二个主键更改为 1,这与之前的相反。

100%确定函数生效后不会发生任何碰撞,但我想知道,如何实现?

事实上,我可以使用合成主键作为 BIGSERIAL,但仍然需要 UNIQUE 包含这两个 ID,因此它不会执行把戏,不幸的是。

您可以将约束声明为可延迟的,例如主键:

CREATE TABLE elbat (id int,
                    nmuloc int,
                    PRIMARY KEY (id)
                                DEFERRABLE);

然后您可以在事务中使用 SET CONSTRAINTS 将可延迟约束设置为已延迟。这意味着它们可以在交易期间暂时被违反,但必须在交易的 COMMIT.

履行

假设我们的示例中有一些数据 table:

INSERT INTO elbat (id,
                   nmuloc)
                  VALUES (1,
                          1),
                         (2,
                          2);

我们现在可以像这样切换 ID:

BEGIN TRANSACTION;

SET CONSTRAINTS ALL DEFERRED;

UPDATE elbat
       SET id = 2
       WHERE nmuloc = 1;

SELECT *
       FROM elbat;
       
UPDATE elbat
       SET id = 1
       WHERE nmuloc = 2;
       
COMMIT;

即使 ID 在第一个 UPDATE 之后都是 2,也没有错误。
db<>fiddle

更多信息可以在文档中找到,例如在 CREATE TABLE (or ALTER TABLE) and SET CONSTRAINTS.