TL; DR
Ecco una versione in cui non è necessario che un umano legga un valore e lo digiti da solo.
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');
Un'altra opzione sarebbe quella di utilizzare il riutilizzabile Function
condiviso alla fine di questa risposta.
Una soluzione non interattiva
Aggiungendo solo alle altre due risposte, per quelli di noi che hanno bisogno di Sequence
crearli con uno script non interattivo , mentre ad esempio patchano un DB live.
Cioè, quando non vuoi SELECT
il valore manualmente e digitalo tu stesso in CREATE
un'istruzione successiva .
In breve, si può non fare:
CREATE SEQUENCE foo_a_seq
START WITH ( SELECT max(a) + 1 FROM foo );
... poiché la START [WITH]
clausola in si CREATE SEQUENCE
aspetta un valore , non una sottoquery.
Nota: Come regola generale, che si applica a tutti i non-CRUD ( vale a dire : qualcosa di diverso INSERT
, SELECT
, UPDATE
, DELETE
) le dichiarazioni con pgSQL per quanto ne so.
Tuttavia, setval()
fa! Quindi, quanto segue va assolutamente bene:
SELECT setval('foo_a_seq', max(a)) FROM foo;
Se non ci sono dati e non (vuoi) saperlo, usa coalesce()
per impostare il valore predefinito:
SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
-- ^ ^ ^
-- defaults to: 0
Tuttavia, avere il valore della sequenza corrente impostato su 0
è maldestro, se non illegale.
L'utilizzo della forma a tre parametri di setval
sarebbe più appropriato:
-- vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
-- ^ ^
-- is_called
L'impostazione del terzo parametro opzionale di setval
a false
impedirà al successivo nextval
di avanzare nella sequenza prima di restituire un valore, e quindi:
il successivo nextval
restituirà esattamente il valore specificato e l'avanzamento della sequenza inizia con quanto segue nextval
.
- da questa voce nella documentazione
In una nota non correlata, puoi anche specificare la colonna che possiede Sequence
direttamente con CREATE
, non devi modificarla in seguito:
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
In sintesi:
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');
Usare un Function
In alternativa, se hai intenzione di farlo per più colonne, potresti optare per l'utilizzo di un file 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;
Usalo così:
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-tipo è ora legacy , soppiantato dalla nuovaGENERATED … AS IDENTITY
funzionalità definita in SQL: 2003 , in Postgres 10 e versioni successive. Vedi spiegazione .