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 Function
compartilhado 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 Sequence
s criados por um script não interativo , enquanto corrige um banco de dados live-ish, por exemplo.
Isto é, quando você não quer SELECT
o valor manualmente e o digita em uma CREATE
instruçã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 SEQUENCE
espera 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 setval
seria 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 setval
para false
evitará que o próximo nextval
avance na sequência antes de retornar um valor e, portanto:
o próximo nextval
retornará 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 Sequence
diretamente 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
SERIAL
pseudo-tipo agora é legado , suplantado pelo novoGENERATED … AS IDENTITY
recurso definido no SQL: 2003 , no Postgres 10 e posterior. Veja a explicação .