Postgres SQL,如何在两个连续 ID 之间复制/插入时自动增加 ID?

Postgres SQL, how to automatically increment ID when duplicate / insert between two sequential ID's?

我有一个 table 以 SERIAL ID 作为主键。 如您所知,序列号会自动递增,我的 table.

需要此功能
ID | info
---------
1  | xxx
2  | xxx
3  | xxx

关于排序问题,我想在 1 和 2 之间插入一行。因此为新行指定一个等于 2 的 ID,并希望其他 ID 自动递增到 3,4。如果我执行这样的查询,我会收到重复键错误。

有没有办法让它成为可能,也许将 SERIAL ID 更改为其他类型?

你所描述的不是大多数人认为的ID,它应该是一个永久的、任意的标识符,自增列只是一种创建唯一值的便捷方式。例如,您不能将不断变化的值用作外键,因此可能需要两列。

但是,您所描述的任务只需一个普通的 Integer 列即可轻松实现,我们称其为 "position",因为这似乎是此行为的更合乎逻辑的标签。

算法简单:

  1. 通过将所有现有元素向上移动一位来为新值创建 space。
  2. 插入新元素。

在 SQL 中,看起来像这样,在位置 42 处插入:

UPDATE items SET position=position + 1 WHERE position >= 42;
INSERT INTO items ( position, name ) VALUES ( 42, 'Answer' );

您可以将其包装在服务器上的 SQL 函数中,并将其包装在事务中以防止并发插入相互混淆。

请注意,默认情况下,position 列上的 PRIMARY KEYUNIQUE 约束在更新期间可能会失效,因为对每一行的更改都是单独验证的。要解决这个问题,您可以使用 "deferrable constraint";即使在 "immediate" 模式下,也只会在语句末尾检查,因此更新不会违反它。

CONSTRAINT uq_position UNIQUE (position) DEFERRABLE INITIALLY IMMEDIATE

另请注意,Serial 列不必是唯一的,因此您仍然可以将默认值设置为自动递增。但是,它不会注意到您插入了额外的值,因此您需要在手动插入后重置序列:

SELECT setval(
     pg_get_serial_sequence('items', 'position'),
     ( SELECT max(position) FROM items )
);

这里是live demo putting it all together。 (SQLFiddle 似乎有一个不是 dropping/resetting 序列的错误,使得 id 值看起来很奇怪。)