Inspiré par les autres réponses ici, j'ai créé une fonction SQL pour effectuer une migration de séquence. La fonction déplace une séquence de touches primaires vers une nouvelle séquence contiguë commençant par n'importe quelle valeur (> = 1) à l'intérieur ou à l'extérieur de la plage de séquence existante.
J'explique ici comment j'ai utilisé cette fonction dans une migration de deux bases de données avec le même schéma mais des valeurs différentes dans une seule base de données.
Tout d'abord, la fonction (qui imprime les commandes SQL générées afin de savoir ce qui se passe réellement):
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 fonction migrate_pkey_sequence
prend les arguments suivants:
arg_table
: nom de la table (par exemple 'example'
)
arg_column
: nom de la colonne de clé primaire (par exemple 'id'
)
arg_sequence
: nom de la séquence (par exemple 'example_id_seq'
)
arg_next_value
: valeur suivante de la colonne après la migration
Il effectue les opérations suivantes:
- Déplacez les valeurs de clé primaire vers une plage libre. Je suppose que cela
nextval('example_id_seq')
suit max(id)
et que la séquence commence par 1. Cela gère également le cas où arg_next_value > max(id)
.
- Déplacez les valeurs de clé primaire vers la plage contiguë en commençant par
arg_next_value
. L'ordre des valeurs clés est conservé mais les trous dans la plage ne sont pas conservés.
- Imprimez la valeur suivante qui suivrait dans la séquence. Ceci est utile si vous souhaitez migrer les colonnes d'une autre table et fusionner avec celle-ci.
Pour démontrer, nous utilisons une séquence et un tableau définis comme suit (par exemple en utilisant 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)
);
Ensuite, nous insérons quelques valeurs (commençant, par exemple, à 3):
# ALTER SEQUENCE example_id_seq RESTART WITH 3;
# INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
-- id: 3, 4, 5
Enfin, nous migrons les example.id
valeurs pour commencer par 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)
Le résultat:
# SELECT * FROM example;
id
----
1
2
3
(3 rows)