TL; DR
Aqui está uma versão em que você não precisa de um ser humano para ler um valor e digitá-lo por conta própria.
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');
Outra opção seria empregar o reaproveitável Functioncompartilhado ao final desta resposta.
Uma solução não interativa
Apenas adicionando as outras duas respostas, para aqueles de nós que precisam ter esses Sequences criados por um script não interativo , enquanto corrige um banco de dados live-ish, por exemplo.
Isto é, quando você não quer SELECTo valor manualmente e o digita em uma CREATEinstrução subsequente .
Resumindo, você não pode fazer:
CREATE SEQUENCE foo_a_seq
START WITH ( SELECT max(a) + 1 FROM foo );
... já que a START [WITH]cláusula em CREATE SEQUENCEespera um valor , não uma subconsulta.
Nota: Como regra geral, que se aplica a todos os não-CRUD ( ou seja : nada além de INSERT, SELECT, UPDATE, DELETE) declarações em pgSQL AFAIK.
No entanto, setval()sim! Portanto, o seguinte está absolutamente correto:
SELECT setval('foo_a_seq', max(a)) FROM foo;
Se não houver dados e você não (quiser) saber sobre eles, use coalesce()para definir o valor padrão:
SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
-- ^ ^ ^
-- defaults to: 0
No entanto, ter o valor da sequência atual definido como 0é desajeitado, se não ilegal.
Usar a forma de três parâmetros de setvalseria mais apropriado:
-- vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
-- ^ ^
-- is_called
Definir o terceiro parâmetro opcional de setvalpara falseevitará que o próximo nextvalavance na sequência antes de retornar um valor e, portanto:
o próximo nextvalretornará exatamente o valor especificado e o avanço da sequência começará com o seguinte nextval.
- desta entrada na documentação
Em uma nota não relacionada, você também pode especificar a coluna proprietária de Sequencediretamente com CREATE, você não precisa alterá-la mais tarde:
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
Em suma:
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');
Usando um Function
Como alternativa, se você planeja fazer isso para várias colunas, pode optar por usar um real 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;
Use-o assim:
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
SERIALpseudo-tipo agora é legado , suplantado pelo novoGENERATED … AS IDENTITYrecurso definido no SQL: 2003 , no Postgres 10 e posterior. Veja a explicação .