TL; DR
Voici une version où vous n'avez pas besoin d'un humain pour lire une valeur et la saisir lui-même.
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
Une autre option serait d'utiliser le Function
partage réutilisable à la fin de cette réponse.
Une solution non interactive
Il suffit d'ajouter aux deux autres réponses, pour ceux d'entre nous qui ont besoin de Sequence
créer ces s par un script non interactif , tout en corrigeant une base de données en direct par exemple.
Autrement dit, lorsque vous ne voulez pas SELECT
la valeur manuellement et tapez-la vous-même dans une CREATE
instruction ultérieure .
Bref, vous ne pouvez pas faire:
CREATE SEQUENCE foo_a_seq
START WITH ( SELECT max(a) + 1 FROM foo );
... puisque la START [WITH]
clause in CREATE SEQUENCE
attend une valeur , pas une sous-requête.
Note: En règle générale, applicable à tous les non-CRUD ( c. -à- : autre chose que INSERT
, SELECT
, UPDATE
, DELETE
) les déclarations pgSQL AFAIK.
Mais setval()
oui! Ainsi, ce qui suit est absolument parfait:
SELECT setval('foo_a_seq', max(a)) FROM foo;
S'il n'y a pas de données et que vous ne (voulez) pas le savoir, utilisez coalesce()
pour définir la valeur par défaut:
SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
-- ^ ^ ^
-- defaults to: 0
Cependant, avoir la valeur de séquence actuelle définie sur 0
est maladroit, voire illégal.
L'utilisation de la forme à trois paramètres de setval
serait plus appropriée:
-- vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
-- ^ ^
-- is_called
La définition du troisième paramètre optionnel de setval
to false
empêchera le suivant nextval
d'avancer la séquence avant de renvoyer une valeur, et donc:
le suivant nextval
renverra exactement la valeur spécifiée et l'avancement de la séquence commence par ce qui suit nextval
.
- à partir de cette entrée dans la documentation
Sur une note non liée, vous pouvez également spécifier la colonne possédant Sequence
directement avec CREATE
, vous n'avez pas à la modifier plus tard:
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
En résumé:
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
ALTER TABLE foo ALTER COLUMN a SET DEFAULT nextval('foo_a_seq');
Utilisant un Function
Sinon, si vous prévoyez de le faire pour plusieurs colonnes, vous pouvez opter pour l'utilisation d'un fichier réel Function
.
CREATE OR REPLACE FUNCTION make_into_serial(table_name TEXT, column_name TEXT) RETURNS INTEGER AS $$
DECLARE
start_with INTEGER;
sequence_name TEXT;
BEGIN
sequence_name := table_name || '_' || column_name || '_seq';
EXECUTE 'SELECT coalesce(max(' || column_name || '), 0) + 1 FROM ' || table_name
INTO start_with;
EXECUTE 'CREATE SEQUENCE ' || sequence_name ||
' START WITH ' || start_with ||
' OWNED BY ' || table_name || '.' || column_name;
EXECUTE 'ALTER TABLE ' || table_name || ' ALTER COLUMN ' || column_name ||
' SET DEFAULT nextVal(''' || sequence_name || ''')';
RETURN start_with;
END;
$$ LANGUAGE plpgsql VOLATILE;
Utilisez-le comme ceci:
INSERT INTO foo (data) VALUES ('asdf');
-- ERROR: null value in column "a" violates not-null constraint
SELECT make_into_serial('foo', 'a');
INSERT INTO foo (data) VALUES ('asdf');
-- OK: 1 row(s) affected
SERIAL
pseudo-type est désormais hérité , supplanté par la nouvelleGENERATED … AS IDENTITY
fonctionnalité définie dans SQL: 2003 , dans Postgres 10 et versions ultérieures. Voir l' explication .