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

时间:2012-02-02 07:56:55

标签: postgresql auto-increment

我在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。但是,在这种情况下,表的自动增量指针不会更新。因此,在第3个插入中,我收到错误:

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。但我不知道该怎么做。

3 个答案:

答案 0 :(得分:78)

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

您可以通过使用明确提供的值手动setting上次插入后序列的值来解决此问题:

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

答案 1 :(得分:3)

在最新版本的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;

答案 2 :(得分:2)

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

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