Inspirado pelas outras respostas aqui, criei uma função SQL para fazer uma migração de sequência. A função move uma sequência de teclas primárias para uma nova sequência contígua iniciando com qualquer valor (> = 1) dentro ou fora do intervalo de sequências existente.
Explico aqui como usei essa função na migração de dois bancos de dados com o mesmo esquema, mas com valores diferentes em um banco de dados.
Primeiro, a função (que imprime os comandos SQL gerados para que fique claro o que realmente está acontecendo):
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;
A função migrate_pkey_sequence
aceita os seguintes argumentos:
arg_table
: nome da tabela (por exemplo 'example'
)
arg_column
: nome da coluna da chave primária (por exemplo 'id'
)
arg_sequence
: nome da sequência (por exemplo 'example_id_seq'
)
arg_next_value
: próximo valor para a coluna após a migração
Ele executa as seguintes operações:
- Mova os valores da chave primária para um intervalo livre. Suponho que se
nextval('example_id_seq')
segue max(id)
e que a sequência começa com 1. Isso também lida com o caso em que arg_next_value > max(id)
.
- Mova os valores da chave primária para o intervalo contíguo que começa com
arg_next_value
. A ordem dos valores-chave é preservada, mas os furos no intervalo não são preservados.
- Imprima o próximo valor a seguir na sequência. Isso é útil se você deseja migrar as colunas de outra tabela e mesclar com esta.
Para demonstrar, usamos uma sequência e uma tabela definidas da seguinte forma (por exemplo, usando 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)
);
Em seguida, inserimos alguns valores (começando, por exemplo, em 3):
# ALTER SEQUENCE example_id_seq RESTART WITH 3;
# INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
-- id: 3, 4, 5
Por fim, migramos os example.id
valores para começar com 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)
O resultado:
# SELECT * FROM example;
id
----
1
2
3
(3 rows)