How to reset sequence in postgres and fill id column with new data?

前端 未结 13 2188
星月不相逢
星月不相逢 2020-11-27 10:47

I have a table with over million rows. I need to reset sequence and reassign id column with new values (1, 2, 3, 4... etc...). Is any easy way to do that?

13条回答
  •  没有蜡笔的小新
    2020-11-27 11:22

    Inspired by the other answers here, I created an SQL function to do a sequence migration. The function moves a primary key sequence to a new contiguous sequence starting with any value (>= 1) either inside or outside the existing sequence range.

    I explain here how I used this function in a migration of two databases with the same schema but different values into one database.

    First, the function (which prints the generated SQL commands so that it is clear what is actually happening):

    CREATE OR REPLACE FUNCTION migrate_pkey_sequence
      ( arg_table      text
      , arg_column     text
      , arg_sequence   text
      , arg_next_value bigint  -- Must be >= 1
      )
    RETURNS int AS $$
    DECLARE
      result int;
      curr_value bigint = arg_next_value - 1;
      update_column1 text := format
        ( 'UPDATE %I SET %I = nextval(%L) + %s'
        , arg_table
        , arg_column
        , arg_sequence
        , curr_value
        );
      alter_sequence text := format
        ( 'ALTER SEQUENCE %I RESTART WITH %s'
        , arg_sequence
        , arg_next_value
        );
      update_column2 text := format
        ( 'UPDATE %I SET %I = DEFAULT'
        , arg_table
        , arg_column
        );
      select_max_column text := format
        ( 'SELECT coalesce(max(%I), %s) + 1 AS nextval FROM %I'
        , arg_column
        , curr_value
        , arg_table
        );
    BEGIN
      -- Print the SQL command before executing it.
      RAISE INFO '%', update_column1;
      EXECUTE update_column1;
      RAISE INFO '%', alter_sequence;
      EXECUTE alter_sequence;
      RAISE INFO '%', update_column2;
      EXECUTE update_column2;
      EXECUTE select_max_column INTO result;
      RETURN result;
    END $$ LANGUAGE plpgsql;
    

    The function migrate_pkey_sequence takes the following arguments:

    1. arg_table: table name (e.g. 'example')
    2. arg_column: primary key column name (e.g. 'id')
    3. arg_sequence: sequence name (e.g. 'example_id_seq')
    4. arg_next_value: next value for the column after migration

    It performs the following operations:

    1. Move the primary key values to a free range. I assume that nextval('example_id_seq') follows max(id) and that the sequence starts with 1. This also handles the case where arg_next_value > max(id).
    2. Move the primary key values to the contiguous range starting with arg_next_value. The order of key values are preserved but holes in the range are not preserved.
    3. Print the next value that would follow in the sequence. This is useful if you want to migrate the columns of another table and merge with this one.

    To demonstrate, we use a sequence and table defined as follows (e.g. using psql):

    # CREATE SEQUENCE example_id_seq
      START WITH 1
      INCREMENT BY 1
      NO MINVALUE
      NO MAXVALUE
      CACHE 1;
    
    # CREATE TABLE example
      ( id bigint NOT NULL DEFAULT nextval('example_id_seq'::regclass)
      );
    

    Then, we insert some values (starting, for example, at 3):

    # ALTER SEQUENCE example_id_seq RESTART WITH 3;
    # INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
    -- id: 3, 4, 5
    

    Finally, we migrate the example.id values to start with 1.

    # SELECT migrate_pkey_sequence('example', 'id', 'example_id_seq', 1);
    INFO:  00000: UPDATE example SET id = nextval('example_id_seq') + 0
    INFO:  00000: ALTER SEQUENCE example_id_seq RESTART WITH 1
    INFO:  00000: UPDATE example SET id = DEFAULT
     migrate_pkey_sequence
    -----------------------
                         4
    (1 row)
    

    The result:

    # SELECT * FROM example;
     id
    ----
      1
      2
      3
    (3 rows)
    

提交回复
热议问题