我有一个表,其中SERIAL ID为主键。 如您所知,序列ID会自动递增,我在表格中需要此功能。
ID | info
---------
1 | xxx
2 | xxx
3 | xxx
对于订购事宜,我想在1和2之间插入一行。这样就给新行一个ID等于2的ID,并希望其他ID自动增加到3,4。如果我执行这样的查询,我会收到重复的密钥错误。
有没有办法让它成为可能,可能会将SERIAL ID更改为其他类型?
答案 0 :(得分:2)
您所描述的并不是大多数人会考虑的ID,而ID应该是永久性和任意的标识符,自动增量列只是创建唯一值的便捷方式。例如,您无法使用保持更改为外键的值,因此可能需要两列。
但是,您所描述的任务很容易通过普通的Integer列来实现,我们称之为“位置”,因为这似乎是这种行为的更合理的标签。
算法很简单:
在SQL中,看起来像这样,插入位置42:
UPDATE items SET position=position + 1 WHERE position >= 42;
INSERT INTO items ( position, name ) VALUES ( 42, 'Answer' );
您可以将它包装在服务器上的SQL函数中,并将其包装在事务中,以防止并发插入相互混淆。
请注意,默认情况下,PRIMARY KEY
列上的UNIQUE
或position
约束可能会在更新期间失效,因为每行的更改都会单独验证。要解决这个问题,您可以使用“可延迟约束”;即使在“立即”模式下,也只会在声明结尾处进行检查,因此更新不会违反。
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
值看起来很奇怪。)