TL; DR
Aquí hay una versión en la que no necesita un ser humano para leer un valor y escribirlo ellos mismos.
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');
Otra opción sería emplear el Functioncompartido reutilizable al final de esta respuesta.
Una solución no interactiva
Solo agregando a las otras dos respuestas, para aquellos de nosotros que necesitamos que estos mensajes de correo electrónico sean Sequencecreados por un script no interactivo , mientras parcheamos una base de datos en vivo, por ejemplo.
Es decir, cuando no desea SELECTel valor manualmente y lo escribe usted mismo en una CREATEdeclaración posterior .
En resumen, se puede no hacer:
CREATE SEQUENCE foo_a_seq
START WITH ( SELECT max(a) + 1 FROM foo );
... ya que la START [WITH]cláusula en CREATE SEQUENCEespera un valor , no una subconsulta.
Nota: Como regla general, que se aplica a todos los no-CRUD ( es decir : que no sea nada INSERT, SELECT, UPDATE, DELETEdeclaraciones en) pgSQL yo sepa.
¡Sin embargo, lo setval()hace! Por lo tanto, lo siguiente está absolutamente bien:
SELECT setval('foo_a_seq', max(a)) FROM foo;
Si no hay datos y usted no (quiere) saberlo, use coalesce()para establecer el valor predeterminado:
SELECT setval('foo_a_seq', coalesce(max(a), 0)) FROM foo;
-- ^ ^ ^
-- defaults to: 0
Sin embargo, tener el valor de secuencia actual establecido en 0es torpe, si no ilegal.
Usar la forma de tres parámetros de setvalsería más apropiado:
-- vvv
SELECT setval('foo_a_seq', coalesce(max(a), 0) + 1, false) FROM foo;
-- ^ ^
-- is_called
Establecer el tercer parámetro opcional de setvalto falseevitará que el siguiente nextvalavance en la secuencia antes de devolver un valor, y así:
el siguiente nextvaldevolverá exactamente el valor especificado, y el avance de la secuencia comienza con lo siguiente nextval.
- de esta entrada en la documentación
En una nota no relacionada, también puede especificar la columna a la que pertenece Sequencedirectamente CREATE, no tiene que modificarla más tarde:
CREATE SEQUENCE foo_a_seq OWNED BY foo.a;
En resumen:
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 un Function
Alternativamente, si planea hacer esto para varias columnas, puede optar por usar un archivo 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;
Úselo así:
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 ahora es heredado , reemplazado por la nuevaGENERATED … AS IDENTITYcaracterística definida en SQL: 2003 , en Postgres 10 y posteriores. Ver explicación .