postgres 自动增量未在显式 id 插入时更新

Posted

技术标签:

【中文标题】postgres 自动增量未在显式 id 插入时更新【英文标题】:postgres autoincrement not updated on explicit id inserts 【发布时间】:2012-02-24 21:37:36 【问题描述】:

我在 postgres 中有下表:

CREATE TABLE "test" (
    "id" serial NOT NULL PRIMARY KEY,
    "value" text
)

我正在做以下插入:

insert into test (id, value) values (1, 'alpha')
insert into test (id, value) values (2, 'beta')

insert into test (value) values ('gamma')

在前 2 个插入中,我明确提到了 id。但是,在这种情况下不会更新表的自动增量指针。因此在第三次插入中我得到了错误:

ERROR:  duplicate key value violates unique constraint "test_pkey"
DETAIL:  Key (id)=(1) already exists.

在 MyISAM 和 INNODB 引擎中,我从未在 mysql 中遇到过这个问题。显式与否,mysql 总是根据最大行 id 更新自增指针。

postgres 中这个问题的解决方法是什么?我需要它,因为我想对表中的某些 id 进行更严格的控制。

更新: 我需要它,因为对于某些值,我需要一个固定的 ID。对于其他新条目,我不介意创建新条目。

我认为,只要我明确插入 id,手动将 nextval 指针递增到 max(id) + 1 即可。但我不知道该怎么做。

【问题讨论】:

【参考方案1】:

这就是它应该如何工作的 - next_val('test_id_seq') 仅在系统需要此列的值并且您没有提供值时才被调用。如果您提供值,则不会执行此类调用,因此序列不会“更新”。

您可以通过手动 setting 使用显式提供的值插入最后一次插入后的序列值来解决此问题:

SELECT setval('test_id_seq', (SELECT MAX(id) from "test"));

序列的名称是自动生成的,并且始终为tablename_columnname_seq

【讨论】:

@milen-a-radev +1,谢谢! 奇怪的行为。我也来自 MySQL。谢谢! 我遇到了同样的问题,在你和ruby-forum.com/topic/64428#72333 之间(你基本上说同样的话)你已经解决了我的问题。谢谢。 我做了所有这一切,表中的 ID 和 seq 最后都为 3,但是当我发布新数据时,完整性错误会引发说 id 为 2 违反唯一约束?我破坏了我的数据库吗?【参考方案2】:

在最新版本的 Django 中,文档中讨论了这个主题:

Django 使用 PostgreSQL 的 SERIAL data type 来存储自增 主键。 SERIAL 列填充了来自sequence 的值 跟踪下一个可用值。手动分配一个 自动递增字段的值不会更新该字段的 序列,这可能会在以后引起冲突。

参考:https://docs.djangoproject.com/en/dev/ref/databases/#manually-specified-autoincrement-pk

还有一个管理命令 manage.py sqlsequencereset app_label ... 能够生成 SQL 语句以重置给定应用程序名称的序列

参考:https://docs.djangoproject.com/en/dev/ref/django-admin/#django-admin-sqlsequencereset

例如这些SQL语句是由manage.py sqlsequencereset my_app_in_my_project生成的:

BEGIN;
SELECT setval(pg_get_serial_sequence('"my_project_aaa"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "my_project_aaa";
SELECT setval(pg_get_serial_sequence('"my_project_bbb"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "my_project_bbb";
SELECT setval(pg_get_serial_sequence('"my_project_ccc"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "my_project_ccc";
COMMIT;

【讨论】:

【参考方案3】:

可以使用触发器自动完成。这样您就可以确保始终将最大值用作下一个默认值。

CREATE OR REPLACE FUNCTION set_serial_id_seq()
RETURNS trigger AS
$BODY$
  BEGIN
   EXECUTE (FORMAT('SELECT setval(''%s_%s_seq'', (SELECT MAX(%s) from %s));',
   TG_TABLE_NAME,
   TG_ARGV[0],
   TG_ARGV[0],
   TG_TABLE_NAME));
    RETURN OLD;
   END;
$BODY$
LANGUAGE plpgsql;  

CREATE TRIGGER set_mytable_id_seq
AFTER INSERT OR UPDATE OR DELETE
ON mytable
FOR EACH STATEMENT
  EXECUTE PROCEDURE  set_serial_id_seq('mytable_id');

该函数可以重复用于多个表。将“mytable”更改为感兴趣的表。

有关触发器的更多信息:

https://www.postgresql.org/docs/9.1/plpgsql-trigger.html

https://www.postgresql.org/docs/9.1/sql-createtrigger.html

【讨论】:

以上是关于postgres 自动增量未在显式 id 插入时更新的主要内容,如果未能解决你的问题,请参考以下文章

使用 select 和 autoincrement 插入 postgres 表

WITH之后的SELECT如果在显式事务中则不返回任何行

Prisma:在显式多对多关系中创建或连接记录

MySQL 多行插入是不是获取顺序自动增量 ID?

POSTGRES - 使用 ON CONFLICT DO NOTHING 防止串行增量 [重复]

带有Python的Mysql无法插入具有自动增量ID的记录,为啥?