Inspiriert von den anderen Antworten hier habe ich eine SQL-Funktion erstellt, um eine Sequenzmigration durchzuführen. Die Funktion verschiebt eine Primärschlüsselfolge in eine neue zusammenhängende Folge, beginnend mit einem beliebigen Wert (> = 1) innerhalb oder außerhalb des vorhandenen Sequenzbereichs.
Ich erkläre hier, wie ich diese Funktion bei einer Migration von zwei Datenbanken mit demselben Schema, aber unterschiedlichen Werten in eine Datenbank verwendet habe.
Zunächst die Funktion (die die generierten SQL-Befehle druckt, damit klar ist, was tatsächlich passiert):
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;
Die Funktion migrate_pkey_sequence
akzeptiert die folgenden Argumente:
arg_table
: Tabellenname (zB 'example'
)
arg_column
: Primärschlüsselspalte Namen (z 'id'
)
arg_sequence
: Sequenzname (zB 'example_id_seq'
)
arg_next_value
: nächster Wert für die Spalte nach der Migration
Es führt die folgenden Operationen aus:
- Verschieben Sie die Primärschlüsselwerte in einen freien Bereich. Ich gehe davon aus, dass das
nextval('example_id_seq')
Folgende folgt max(id)
und dass die Sequenz mit 1 beginnt. Dies behandelt auch den Fall, in dem arg_next_value > max(id)
.
- Verschieben Sie die Primärschlüsselwerte beginnend mit in den zusammenhängenden Bereich
arg_next_value
. Die Reihenfolge der Schlüsselwerte bleibt erhalten, Löcher im Bereich bleiben jedoch nicht erhalten.
- Drucken Sie den nächsten Wert, der in der Sequenz folgen würde. Dies ist nützlich, wenn Sie die Spalten einer anderen Tabelle migrieren und mit dieser zusammenführen möchten.
Zur Demonstration verwenden wir eine Sequenz und Tabelle, die wie folgt definiert sind (z. B. 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)
);
Dann fügen wir einige Werte ein (beginnend zum Beispiel bei 3):
# ALTER SEQUENCE example_id_seq RESTART WITH 3;
# INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
-- id: 3, 4, 5
Schließlich migrieren wir die example.id
Werte, um mit 1 zu beginnen.
# 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)
Das Ergebnis:
# SELECT * FROM example;
id
----
1
2
3
(3 rows)