web-dev-qa-db-fra.com

Comment réinitialiser la séquence dans postgres et remplir la colonne id avec de nouvelles données?

J'ai une table avec plus de millions de lignes. Je dois réinitialiser la séquence et réaffecter la colonne id avec les nouvelles valeurs (1, 2, 3, 4 ... etc ...). Y a-t-il un moyen facile de faire ça?

77
sennin

Si vous ne souhaitez pas conserver l'ordre des identifiants, vous pouvez

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

Je doute qu'il y ait un moyen facile de le faire dans l'ordre de votre choix sans recréer la table entière.

134

Réinitialiser la séquence:

SELECT setval('sequence_name', 0);

Mise à jour des enregistrements actuels:

UPDATE foo SET id = DEFAULT;
34
Frank Heikens

Avec PostgreSQL 8.4 ou plus récent, il n’est plus nécessaire de spécifier le WITH 1. La valeur de départ enregistrée par CREATE SEQUENCE ou la dernière valeur définie par ALTER SEQUENCE START WITH sera utilisée (probablement 1).

Réinitialiser la séquence:

ALTER SEQUENCE seq RESTART;

Puis mettez à jour la colonne ID de la table:

UPDATE foo SET id = DEFAULT;

Source: Documents PostgreSQL

29
Oliver

Les deux solutions fournies ne fonctionnaient pas pour moi;

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

setval('seq', 1) commence la numérotation avec 2 et ALTER SEQUENCE seq START 1 commence également avec 2, car seq.is_called est true (Postgres version 9.0.4)

La solution qui a fonctionné pour moi est la suivante:

> ALTER SEQUENCE seq RESTART WITH 1;
> UPDATE foo SET id = DEFAULT;
15
ivy

Juste pour simplifier et clarifier l'utilisation correcte de ALTER SEQUENCE et SELECT setval pour réinitialiser la séquence:

ALTER SEQUENCE sequence_name RESTART WITH 1;

est équivalent à

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

Chacune des instructions peut être utilisée pour réinitialiser la séquence et vous pouvez obtenir la valeur suivante avec nextval ('nom_de_séquence') comme indiqué par ici aussi:

nextval('sequence_name')
11
Ali Raza Bhayani

Le meilleur moyen de réinitialiser une séquence pour recommencer avec le numéro 1 est d'exécuter les tâches suivantes:

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

Ainsi, par exemple pour la table users, ce serait:

ALTER SEQUENCE users_id_seq RESTART WITH 1
7
jahmed31

FYI: Si vous avez besoin de spécifier une nouvelle valeur de départ entre une plage d’ID (256 à 10000000 par exemple):

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

Pour conserver l'ordre des lignes:

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;
3
alexkovelsky

La simple réinitialisation de la séquence et la mise à jour de toutes les lignes peuvent provoquer des erreurs d'identifiant en double. Dans de nombreux cas, vous devez mettre à jour toutes les lignes deux fois. D'abord avec des identifiants plus élevés pour éviter les doublons, puis avec les identifiants que vous voulez réellement. 

Évitez d’ajouter un montant fixe à tous les identifiants (comme recommandé dans d’autres commentaires). Que se passe-t-il si vous avez plus de lignes que ce montant fixe? En supposant que la valeur suivante de la séquence est supérieure à tous les identifiants des lignes existantes (vous voulez simplement combler les lacunes), je le ferais comme ceci:

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

Dans mon cas, j'ai réalisé ceci avec:

ALTER SEQUENCE table_tabl_id_seq RESTART WITH 6;

Où ma table est nommée table

Même la colonne d'incrémentation automatique n'est pas PK (dans cet exemple, elle s'appelle seq - aka sequence), vous pouvez y parvenir avec un déclencheur: 

DROP TABLE SI 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();
0
Yordan Georgiev

Inspiré par les autres réponses ici, j'ai créé une fonction SQL pour effectuer une migration de séquence. La fonction déplace une séquence de clé primaire vers une nouvelle séquence contiguë commençant par une valeur (> = 1) située à l'intérieur ou à l'extérieur de la plage de séquence existante.

J'explique ici comment j'ai utilisé cette fonction lors de la migration de deux bases de données avec le même schéma mais des valeurs différentes dans une base de données.

Tout d’abord, la fonction (qui affiche les commandes SQL générées de manière à ce que Clarifie ce qui se passe réellement):

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 fonction migrate_pkey_sequence prend les arguments suivants:

  1. arg_table: nom de la table (par exemple 'example')
  2. arg_column: nom de colonne de la clé primaire (par exemple 'id')
  3. arg_sequence: nom de la séquence (par exemple 'example_id_seq')
  4. arg_next_value: valeur suivante pour la colonne après la migration

Il effectue les opérations suivantes:

  1. Déplacez les valeurs de clé primaire vers une plage libre. Je suppose que nextval('example_id_seq') suit max(id) et que la séquence commence Par 1. Ceci traite également le cas où arg_next_value > max(id).
  2. Déplacez les valeurs de clé primaire vers la plage contiguë commençant par arg_next_value. L'ordre des valeurs de clé est conservé, mais les trous de la plage Ne le sont pas.
  3. Imprimer la prochaine valeur qui suivrait dans la séquence. Ceci est utile si vous souhaitez migrer les colonnes d'une autre table et fusionner avec celle-ci.

Pour démontrer, nous utilisons une séquence et une table définies comme suit (par exemple, en utilisant 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)
  );

Ensuite, nous insérons quelques valeurs (en commençant par 3):

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

Enfin, nous migrons les valeurs example.id pour commencer avec 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)

Le résultat:

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