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

时间:2015-05-21 20:08:47

标签: sql postgresql postgresql-9.3

我有一个表,其中SERIAL ID为主键。 如您所知,序列ID会自动递增,我在表格中需要此功能。

ID | info
---------
1  | xxx
2  | xxx
3  | xxx

对于订购事宜,我想在1和2之间插入一行。这样就给新行一个ID等于2的ID,并希望其他ID自动增加到3,4。如果我执行这样的查询,我会收到重复的密钥错误。

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

1 个答案:

答案 0 :(得分:2)

您所描述的并不是大多数人会考虑的ID,而ID应该是永久性和任意的标识符,自动增量列只是创建唯一值的便捷方式。例如,您无法使用保持更改为外键的值,因此可能需要两列。

但是,您所描述的任务很容易通过普通的Integer列来实现,我们称之为“位置”,因为这似乎是这种行为的更合理的标签。

算法很简单:

  1. 通过将所有现有元素上移一个位置,为新值腾出空间。
  2. 插入新元素。
  3. 在SQL中,看起来像这样,插入位置42:

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

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

    请注意,默认情况下,PRIMARY KEY列上的UNIQUEposition约束可能会在更新期间失效,因为每行的更改都会单独验证。要解决这个问题,您可以使用“可延迟约束”;即使在“立即”模式下,也只会在声明结尾处进行检查,因此更新不会违反。

    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似乎有一个错误,它不会丢弃/重置序列,使id值看起来很奇怪。)