postgres autoincrement not updated on explicit id inserts

前端 未结 3 1699
清酒与你
清酒与你 2020-12-04 21:26

I have the following table in postgres:

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

I am doing fol

相关标签:
3条回答
  • 2020-12-04 21:34

    In the recent version of Django, this topic is discussed in the documentation:

    Django uses PostgreSQL’s SERIAL data type to store auto-incrementing primary keys. A SERIAL column is populated with values from a sequence that keeps track of the next available value. Manually assigning a value to an auto-incrementing field doesn’t update the field’s sequence, which might later cause a conflict.

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

    There is also management command manage.py sqlsequencereset app_label ... that is able to generate SQL statements for resetting sequences for the given app name(s)

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

    For example these SQL statements were generated by 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;
    
    0 讨论(0)
  • 2020-12-04 21:36

    That's how it's supposed to work - next_val('test_id_seq') is only called when the system needs a value for this column and you have not provided one. If you provide value no such call is performed and consequently the sequence is not "updated".

    You could work around this by manually setting the value of the sequence after your last insert with explicitly provided values:

    SELECT setval('test_id_seq', (SELECT MAX(id) from "test"));
    
    0 讨论(0)
  • 2020-12-04 21:42

    It can be done automatically using a trigger. This way you are sure that the largest value is always used as the next default value.

    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');
    

    The function can be reused for multiple tables. Change "mytable" to the table of interest.

    For more info regarding triggers:

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

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

    0 讨论(0)
提交回复
热议问题