【问题标题】:Postgres SQL, how to automatically increment ID when duplicate / insert between two sequential ID's?Postgres SQL,在两个顺序ID之间重复/插入时如何自动增加ID?
【发布时间】:2015-05-21 20:08:47
【问题描述】:

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

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

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

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

【问题讨论】:

标签: sql postgresql postgresql-9.3


【解决方案1】:

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

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

算法很简单:

  1. 将所有现有元素上移一个位置,为新值腾出空间。
  2. 插入新元素。

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

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

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

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

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 值看起来很奇怪。)

【讨论】:

  • @Houari 怎么样?每个项目都被向上移动一个位置,除非它们已经是重复的,否则它们怎么会变成重复的?
  • 我现在明白我的问题有多愚蠢,解决问题的方法有多简单。谢谢。
  • @Houari 嗯,很公平,我忘了要检查每一行的约束,而不是每个语句。您必须将任何 UNIQUE 约束设置为 DEFERRABLE。我将编辑我的答案。
  • @Houari 实际上将约束标记为 DEFERRABLE 就足够了,但将其保留为 IMMEDIATE,因为这足以让它在每个语句中检查一次,而不是每行检查一次:sqlfiddle.com/#!15/0f273/4
猜你喜欢
  • 2012-02-24
  • 1970-01-01
  • 2020-06-18
  • 2020-09-23
  • 1970-01-01
  • 2014-11-06
  • 2013-10-31
  • 2010-11-06
相关资源
最近更新 更多