Inspirado por las otras respuestas aquí, creé una función SQL para hacer una migración de secuencia. La función mueve una secuencia de teclas principal a una nueva secuencia contigua que comienza con cualquier valor (> = 1) dentro o fuera del rango de secuencia existente.
Explicaré aquí cómo utilicé esta función en una migración de dos bases de datos con el mismo esquema pero valores diferentes en una base de datos.
Primero, la función (que imprime los comandos SQL generados para que quede claro lo que realmente está sucediendo):
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;
La función migrate_pkey_sequence
toma los siguientes argumentos:
arg_table
: nombre de la tabla (p 'example'
. ej. )
arg_column
: nombre de columna de clave principal (por ejemplo 'id'
)
arg_sequence
: nombre de secuencia (p 'example_id_seq'
. ej. )
arg_next_value
: siguiente valor para la columna después de la migración
Realiza las siguientes operaciones:
- Mueva los valores de la clave primaria a un rango libre. Supongo que
nextval('example_id_seq')
sigue max(id)
y que la secuencia comienza con 1. Esto también maneja el caso donde arg_next_value > max(id)
.
- Mueva los valores de la clave primaria al rango contiguo comenzando por
arg_next_value
. Se conserva el orden de los valores clave, pero no se conservan los agujeros en el rango.
- Imprima el siguiente valor que seguiría en la secuencia. Esto es útil si desea migrar las columnas de otra tabla y fusionarlas con esta.
Para demostrarlo, utilizamos una secuencia y una tabla definidas de la siguiente manera (por ejemplo, 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)
);
Luego, insertamos algunos valores (comenzando, por ejemplo, en 3):
# ALTER SEQUENCE example_id_seq RESTART WITH 3;
# INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
-- id: 3, 4, 5
Finalmente, migramos los example.id
valores para comenzar con 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)
El resultado:
# SELECT * FROM example;
id
----
1
2
3
(3 rows)