您所描述的并不是大多数人认为的 ID,它应该是一个永久且任意的标识符,自动递增列只是创建唯一值的一种便捷方式。例如,您不能将不断变化的值用作外键,因此很可能需要两列。
但是,您所描述的任务很容易通过一个普通的 Integer 列来实现,我们称之为“位置”,因为对于这种行为来说,这似乎是一个更合乎逻辑的标签。
算法很简单:
- 将所有现有元素上移一个位置,为新值腾出空间。
- 插入新元素。
在 SQL 中,它看起来像这样,插入到位置 42:
UPDATE items SET position=position + 1 WHERE position >= 42;
INSERT INTO items ( position, name ) VALUES ( 42, 'Answer' );
您可以将其包装在服务器上的 SQL 函数中,并将其包装在事务中以防止并发插入相互混淆。
请注意,默认情况下,position 列上的PRIMARY KEY 或UNIQUE 约束可能会在更新期间失效,因为对每一行的更改都是单独验证的。为了解决这个问题,您可以使用“可延迟约束”;即使在“立即”模式下,也只会在语句末尾检查,因此更新不会违反它。
CONSTRAINT uq_position UNIQUE (position) DEFERRABLE INITIALLY IMMEDIATE
另请注意,串行列不必是唯一的,因此您仍然可以将默认值设为自动增量。但是,它不会注意到您插入了额外的值,因此您需要在手动插入后重置序列:
SELECT setval(
pg_get_serial_sequence('items', 'position'),
( SELECT max(position) FROM items )
);
这是live demo putting it all together。 (SQLFiddle 似乎有一个错误,它没有删除/重置序列,使得 id 值看起来很奇怪。)