Ispirato dalle altre risposte qui, ho creato una funzione SQL per eseguire una migrazione in sequenza. La funzione sposta una sequenza di tasti primari in una nuova sequenza contigua che inizia con qualsiasi valore (> = 1) all'interno o all'esterno dell'intervallo di sequenza esistente.
Spiego qui come ho utilizzato questa funzione in una migrazione di due database con lo stesso schema ma valori diversi in un database.
Innanzitutto, la funzione (che stampa i comandi SQL generati in modo che sia chiaro cosa sta effettivamente accadendo):
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 funzione migrate_pkey_sequence
accetta i seguenti argomenti:
arg_table
: nome tabella (ad es. 'example'
)
arg_column
: nome della colonna della chiave primaria (ad es 'id'
)
arg_sequence
: nome della sequenza (es. 'example_id_seq'
)
arg_next_value
: valore successivo per la colonna dopo la migrazione
Esegue le seguenti operazioni:
- Spostare i valori della chiave primaria in un intervallo libero. Presumo che
nextval('example_id_seq')
segua max(id)
e che la sequenza inizi con 1. Questo gestisce anche il caso in cui arg_next_value > max(id)
.
- Spostare i valori della chiave primaria nell'intervallo contiguo a partire da
arg_next_value
. L'ordine dei valori chiave viene mantenuto ma i buchi nell'intervallo non vengono preservati.
- Stampa il valore successivo che seguirà nella sequenza. Questo è utile se vuoi migrare le colonne di un'altra tabella e unirle a questa.
Per dimostrare, usiamo una sequenza e una tabella definite come segue (ad esempio utilizzando 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)
);
Quindi, inseriamo alcuni valori (iniziando, ad esempio, da 3):
# ALTER SEQUENCE example_id_seq RESTART WITH 3;
# INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
-- id: 3, 4, 5
Infine, migriamo i example.id
valori per iniziare 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)
Il risultato:
# SELECT * FROM example;
id
----
1
2
3
(3 rows)