¿Cómo restablecer la secuencia en postgres y llenar la columna de identificación con nuevos datos?

126

Tengo una mesa con más de un millón de filas. Necesito restablecer la secuencia y reasignar la columna de identificación con nuevos valores (1, 2, 3, 4 ... etc.). ¿Hay alguna manera fácil de hacer eso?

sennin
fuente
66
La verdadera pregunta: ¿por qué querrías hacer eso? Presumiblemente, la ID es la clave principal, por lo que no hay ningún beneficio en cambiar la clave principal. Una clave primaria es un valor sin sentido (en su caso artificial). "Renumerar" no sirve para nada en una base de datos relacional.
a_horse_with_no_name
2
Inicialmente tenía la aplicación ejecutándose localmente, luego copié los datos en producción. Pero ids no comenzó desde 1. Entonces el pedido resultó de la siguiente manera: 150, 151 ..., 300, 1, 2 ... Y supondría que eventualmente causaría errores de identificación duplicados, si no me hubiera reenumerado los id. Además, ordenar por ides generalmente mejor que ordenar por created_at. Y esto es lo que funcionó para mí .
x-yuri
El punto de hacer esto es para que pueda continuar usando un int regular en lugar de bigint para una clave primaria en una base de datos que continúa incrementando la clave secuencial pero recibe constantemente nuevos datos. Rápidamente se encontrará con el límite entero firmado, y si retener la identificación existente no es importante, este proceso lo devolverá a los números de identificación manejables.
Ben Wilson
Otro uso para esto es la prueba. Desea restablecer una tabla a un estado conocido antes de comenzar cada prueba y eso requiere restablecer los identificadores.
Safa Alai

Respuestas:

203

Si no desea retener el orden de los identificadores, puede

ALTER SEQUENCE seq RESTART WITH 1;
UPDATE t SET idcolumn=nextval('seq');

Dudo que haya una manera fácil de hacerlo en el orden que elija sin recrear toda la tabla.

Michael Krelin - hacker
fuente
44
¿No debería ser eso ALTER SEQUENCE seq RESTART WITH 1;?
Lars Haugseth
55
Esto puede causar identificadores duplicados. Para evitar esto, primero puede establecer todo en valores muy altos: ACTUALIZAR t SET idcolumn = 1000000 + nextval ('seq'); luego ejecute el script anterior.
tahagh
55
SELECT setval('seq', 1, FALSE)debería hacer lo mismo (aquí, el tercer argumento, FALSO, hace la magia, ya que muestra que nextvaldebe ser 1 en lugar de 2)
Vasilen Donchev
@VassilenDontchev, absolutamente.
Michael Krelin - pirata informático
55

Con PostgreSQL 8.4 o posterior, ya no es necesario especificarlo WITH 1. Se usará el valor de inicio que se registró CREATE SEQUENCEo se ajustó por última ALTER SEQUENCE START WITHvez (lo más probable es que sea 1).

Restablecer la secuencia:

ALTER SEQUENCE seq RESTART;

Luego actualice la columna de ID de la tabla:

UPDATE foo SET id = DEFAULT;

Fuente: Documentos de PostgreSQL

Oliver
fuente
3
Esta parece ser la mejor respuesta, ya que evita hacer suposiciones sobre el valor inicial de la secuencia.
perro pastor
La mejor respuesta para mi caso también. Combino esta respuesta con esta , que explica el comando ALTER SEQUENCE ... así que cambié 'seq' por mytable_id_seq donde 'mytable' es el nombre de mi tabla y 'id' es el nombre de mi columna serial
Javi
42

Restablecer la secuencia:

SELECT setval('sequence_name', 0);

Actualización de registros actuales:

UPDATE foo SET id = DEFAULT;
Frank Heikens
fuente
3
La secuencia podría tener un valor mínimo mayor que 0. (¡Y el valor mínimo predeterminado usado por tipo serialy CREATE SEQUENCEes 1!)
brk
18

Ambas soluciones proporcionadas no funcionaron para mí;

> SELECT setval('seq', 0);
ERROR:  setval: value 0 is out of bounds for sequence "seq" (1..9223372036854775807)

setval('seq', 1)comienza la numeración con 2 e ALTER SEQUENCE seq START 1inicia la numeración también con 2, porque seq.is_called es verdadero (Postgres versión 9.0.4)

La solución que funcionó para mí es:

> ALTER SEQUENCE seq RESTART WITH 1;
> UPDATE foo SET id = DEFAULT;
hiedra
fuente
1
El mismo problema aqui. Su solución también funciona para PostgreSQL 8.3.10.
PeqNP
17

Solo para simplificar y aclarar el uso adecuado de ALTER SEQUENCE y SELECT setval para restablecer la secuencia:

ALTER SEQUENCE sequence_name RESTART WITH 1;

es equivalente a

SELECT setval('sequence_name', 1, FALSE);

Cualquiera de las declaraciones puede usarse para restablecer la secuencia y puede obtener el siguiente valor mediante nextval ('nombre_secuencia') como se indica aquí también:

nextval('sequence_name')
Ali Raza Bhayani
fuente
Gracias Ali Acabo de notar que es crucial establecer ese tercer parámetro en falso con la función
setval
14

La mejor manera de restablecer una secuencia para comenzar de nuevo con el número 1 es ejecutar lo siguiente:

ALTER SEQUENCE <tablename>_<id>_seq RESTART WITH 1

Entonces, por ejemplo para la tabla de usuarios sería:

ALTER SEQUENCE users_id_seq RESTART WITH 1
jahmed31
fuente
6

Para retener el orden de las filas:

UPDATE thetable SET rowid=col_serial FROM 
(SELECT rowid, row_number() OVER ( ORDER BY lngid) AS col_serial FROM thetable ORDER BY lngid) AS t1 
WHERE thetable.rowid=t1.rowid;
alexkovelsky
fuente
4

FYI: si necesita especificar un nuevo valor de inicio entre un rango de ID (256 - 10000000 por ejemplo):

SELECT setval('"Sequence_Name"', 
       (SELECT coalesce(MAX("ID"),255) 
           FROM "Table_Name" 
           WHERE "ID" < 10000000 and "ID" >= 256)+1
       ); 
Roca
fuente
2

Solo restablecer la secuencia y actualizar todas las filas puede causar errores de identificación duplicados. En muchos casos, debe actualizar todas las filas dos veces. Primero con identificadores más altos para evitar los duplicados, luego con los identificadores que realmente desea.

Evite agregar una cantidad fija a todos los identificadores (como se recomienda en otros comentarios). ¿Qué sucede si tiene más filas que esta cantidad fija? Suponiendo que el siguiente valor de la secuencia es más alto que todos los identificadores de las filas existentes (solo desea llenar los huecos), lo haría así:

UPDATE table SET id = DEFAULT;
ALTER SEQUENCE seq RESTART;
UPDATE table SET id = DEFAULT;
Franco
fuente
1

En mi caso, logré esto con:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

Donde mi mesa se llama mesa

Diego Santa Cruz Mendezú
fuente
Gracias por incluir un ejemplo concreto con el nombre de su tabla. Las otras respuestas fueron un poco ambiguas.
Brylie Christopher Oxley
0

Inspirado por las otras respuestas aquí, creé una función SQL para hacer una migración de secuencia. La función mueve una secuencia de teclas principal a una nueva secuencia contigua que comienza con cualquier valor (> = 1) dentro o fuera del rango de secuencia existente.

Explicaré aquí cómo utilicé esta función en una migración de dos bases de datos con el mismo esquema pero valores diferentes en una base de datos.

Primero, la función (que imprime los comandos SQL generados para que quede claro lo que realmente está sucediendo):

CREATE OR REPLACE FUNCTION migrate_pkey_sequence
  ( arg_table      text
  , arg_column     text
  , arg_sequence   text
  , arg_next_value bigint  -- Must be >= 1
  )
RETURNS int AS $$
DECLARE
  result int;
  curr_value bigint = arg_next_value - 1;
  update_column1 text := format
    ( 'UPDATE %I SET %I = nextval(%L) + %s'
    , arg_table
    , arg_column
    , arg_sequence
    , curr_value
    );
  alter_sequence text := format
    ( 'ALTER SEQUENCE %I RESTART WITH %s'
    , arg_sequence
    , arg_next_value
    );
  update_column2 text := format
    ( 'UPDATE %I SET %I = DEFAULT'
    , arg_table
    , arg_column
    );
  select_max_column text := format
    ( 'SELECT coalesce(max(%I), %s) + 1 AS nextval FROM %I'
    , arg_column
    , curr_value
    , arg_table
    );
BEGIN
  -- Print the SQL command before executing it.
  RAISE INFO '%', update_column1;
  EXECUTE update_column1;
  RAISE INFO '%', alter_sequence;
  EXECUTE alter_sequence;
  RAISE INFO '%', update_column2;
  EXECUTE update_column2;
  EXECUTE select_max_column INTO result;
  RETURN result;
END $$ LANGUAGE plpgsql;

La función migrate_pkey_sequencetoma los siguientes argumentos:

  1. arg_table: nombre de la tabla (p 'example'. ej. )
  2. arg_column: nombre de columna de clave principal (por ejemplo 'id')
  3. arg_sequence: nombre de secuencia (p 'example_id_seq'. ej. )
  4. arg_next_value: siguiente valor para la columna después de la migración

Realiza las siguientes operaciones:

  1. Mueva los valores de la clave primaria a un rango libre. Supongo que nextval('example_id_seq')sigue max(id)y que la secuencia comienza con 1. Esto también maneja el caso donde arg_next_value > max(id).
  2. Mueva los valores de la clave primaria al rango contiguo comenzando por arg_next_value. Se conserva el orden de los valores clave, pero no se conservan los agujeros en el rango.
  3. Imprima el siguiente valor que seguiría en la secuencia. Esto es útil si desea migrar las columnas de otra tabla y fusionarlas con esta.

Para demostrarlo, utilizamos una secuencia y una tabla definidas de la siguiente manera (por ejemplo, usando psql):

# CREATE SEQUENCE example_id_seq
  START WITH 1
  INCREMENT BY 1
  NO MINVALUE
  NO MAXVALUE
  CACHE 1;
# CREATE TABLE example
  ( id bigint NOT NULL DEFAULT nextval('example_id_seq'::regclass)
  );

Luego, insertamos algunos valores (comenzando, por ejemplo, en 3):

# ALTER SEQUENCE example_id_seq RESTART WITH 3;
# INSERT INTO example VALUES (DEFAULT), (DEFAULT), (DEFAULT);
-- id: 3, 4, 5

Finalmente, migramos los example.idvalores para comenzar con 1.

# SELECT migrate_pkey_sequence('example', 'id', 'example_id_seq', 1);
INFO:  00000: UPDATE example SET id = nextval('example_id_seq') + 0
INFO:  00000: ALTER SEQUENCE example_id_seq RESTART WITH 1
INFO:  00000: UPDATE example SET id = DEFAULT
 migrate_pkey_sequence
-----------------------
                     4
(1 row)

El resultado:

# SELECT * FROM example;
 id
----
  1
  2
  3
(3 rows)
Sean Leather
fuente
0

Incluso la columna de incremento automático no es PK (en este ejemplo se llama seq, también conocida como secuencia), puede lograr eso con un disparador:

DROP TABLE IF EXISTS devops_guide CASCADE;

SELECT 'create the "devops_guide" table'
;
   CREATE TABLE devops_guide (
      guid           UUID NOT NULL DEFAULT gen_random_uuid()
    , level          integer NULL
    , seq            integer NOT NULL DEFAULT 1
    , name           varchar (200) NOT NULL DEFAULT 'name ...'
    , description    text NULL
    , CONSTRAINT pk_devops_guide_guid PRIMARY KEY (guid)
    ) WITH (
      OIDS=FALSE
    );

-- START trg_devops_guide_set_all_seq
CREATE OR REPLACE FUNCTION fnc_devops_guide_set_all_seq()
    RETURNS TRIGGER
    AS $$
       BEGIN
         UPDATE devops_guide SET seq=col_serial FROM
         (SELECT guid, row_number() OVER ( ORDER BY seq) AS col_serial FROM devops_guide ORDER BY seq) AS tmp_devops_guide
         WHERE devops_guide.guid=tmp_devops_guide.guid;

         RETURN NEW;
       END;
    $$ LANGUAGE plpgsql;

 CREATE TRIGGER trg_devops_guide_set_all_seq
  AFTER UPDATE OR DELETE ON devops_guide
  FOR EACH ROW
  WHEN (pg_trigger_depth() < 1)
  EXECUTE PROCEDURE fnc_devops_guide_set_all_seq();
Yordan Georgiev
fuente
-1

Si está utilizando pgAdmin3, expanda 'Secuencias', haga clic con el botón derecho en una secuencia, vaya a 'Propiedades' y en la pestaña 'Definición' cambie 'Valor actual' al valor que desee. No hay necesidad de una consulta.

Dinesh Patil
fuente
3
Su respuesta no agrega valor si al menos no nos dice qué herramienta está utilizando.
11101101b
3
Esta es la forma más fácil, obviamente creo que está diciendo pg admin 3
MvcCmsJon