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


126

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


6
La vraie question: pourquoi diable voudriez-vous faire ça? Vraisemblablement, l'ID est la clé primaire, il n'y a donc aucun avantage à changer la clé primaire. Une clé primaire est une valeur sans signification (dans votre cas, artificielle). "Renuméroter" cela ne sert à rien dans une base de données relationnelle.
a_horse_with_no_name

2
Au départ, l'application fonctionnait localement, puis j'ai copié les données en production. Mais idil n'y a pas commencé à partir de 1. Donc, l'ordre s'est avéré comme suit: 150, 151 ..., 300, 1, 2 ... Et cela causerait éventuellement des erreurs d'identifiant en double, je suppose, si je n'avais pas renuméroté les identifiants. De plus, l'ordre par idest généralement meilleur que l'ordre par created_at. Et voici ce qui a fonctionné pour moi .
x-yuri

Le but de cela est que vous puissiez continuer à utiliser un int normal au lieu de bigint pour une clé primaire dans une base de données qui continue d'incrémenter la clé séquentielle mais reçoit constamment de nouvelles données. Vous atteindrez rapidement la limite des nombres entiers signés, et si la conservation de l'identifiant existant n'est pas importante, ce processus vous ramènera à des numéros d'identifiant gérables.
Ben Wilson

Une autre utilisation pour cela est le test. Vous souhaitez réinitialiser une table à un état connu avant de démarrer chaque test et qui nécessite la réinitialisation des identifiants.
Safa Alai

Réponses:


204

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.


4
Cela ne devrait-il pas être le cas ALTER SEQUENCE seq RESTART WITH 1;?
Lars Haugseth

5
Cela peut entraîner des identifiants en double. Pour éviter cela, vous pouvez d'abord régler tous sur des valeurs très élevées: UPDATE t SET idcolumn = 1000000 + nextval ('seq'); puis exécutez le script ci-dessus.
tahagh

5
SELECT setval('seq', 1, FALSE)devrait faire la même chose (ici, le troisième argument, FALSE, fait la magie, car il montre que cela nextvaldoit être 1 au lieu de 2)
Vasilen Donchev

@VassilenDontchev, absolument.
Michael Krelin - hacker

55

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 qui a été enregistrée CREATE SEQUENCEou réglée en dernier par ALTER SEQUENCE START WITHsera utilisée (ce sera très probablement 1).

Réinitialisez la séquence:

ALTER SEQUENCE seq RESTART;

Ensuite, mettez à jour la colonne ID de la table:

UPDATE foo SET id = DEFAULT;

Source: documentation PostgreSQL


3
Cela semble être la meilleure réponse car cela évite de faire des hypothèses sur la valeur de départ de la séquence.
chien de berger

Meilleure réponse pour mon cas aussi. Je combine cette réponse avec celle-ci , ce qui explique la commande ALTER SEQUENCE ... j'ai donc changé 'seq' par mytable_id_seq où 'mytable' est le nom de ma table et 'id' est le nom de ma colonne série
Javi

42

Réinitialisez la séquence:

SELECT setval('sequence_name', 0);

Mise à jour des enregistrements actuels:

UPDATE foo SET id = DEFAULT;

3
La séquence peut avoir une valeur minimale supérieure à 0. (Et la valeur minimale par défaut utilisée par le type serialet CREATE SEQUENCEest 1!)
brk

18

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 par 2, et ALTER SEQUENCE seq START 1commence également la numérotation par 2, car seq.is_called est vrai (Postgres version 9.0.4)

La solution qui a fonctionné pour moi est:

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

1
Même problème ici. Votre solution fonctionne également pour PostgreSQL 8.3.10.
PeqNP

17

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);

L'une ou l'autre des instructions peut être utilisée pour réinitialiser la séquence et vous pouvez obtenir la valeur suivante par nextval ('sequence_name') comme indiqué ici également:

nextval('sequence_name')

Merci Ali. Je viens de remarquer qu'il est crucial de définir ce 3e paramètre sur false avec la fonction
setval

14

La meilleure façon de réinitialiser une séquence pour recommencer avec le numéro 1 est d'exécuter ce qui suit:

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

Ainsi, par exemple pour la table des utilisateurs, ce serait:

ALTER SEQUENCE users_id_seq RESTART WITH 1

6

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;

4

FYI: Si vous devez 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
       ); 

2

La simple réinitialisation de la séquence et la mise à jour de toutes les lignes peuvent entraîner des erreurs d'ID 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.

Veuillez éviter 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:

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

1

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


Merci d'avoir inclus un exemple concret avec le nom de votre table. Les autres réponses étaient un peu trop ambiguës.
Brylie Christopher Oxley

0

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 touches primaires vers une nouvelle séquence contiguë commençant par n'importe quelle valeur (> = 1) à l'intérieur ou à l'extérieur de la plage de séquence existante.

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

Tout d'abord, la fonction (qui imprime les commandes SQL générées afin de savoir 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_sequenceprend les arguments suivants:

  1. arg_table: nom de la table (par exemple 'example')
  2. arg_column: nom de la colonne de clé primaire (par exemple 'id')
  3. arg_sequence: nom de la séquence (par exemple 'example_id_seq')
  4. arg_next_value: valeur suivante de 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 cela nextval('example_id_seq')suit max(id)et que la séquence commence par 1. Cela gère également le cas où arg_next_value > max(id).
  2. Déplacez les valeurs de clé primaire vers la plage contiguë en commençant par arg_next_value. L'ordre des valeurs clés est conservé mais les trous dans la plage ne sont pas conservés.
  3. Imprimez la valeur suivante 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 un tableau définis 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 (commençant, par exemple, à 3):

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

Enfin, nous migrons les example.idvaleurs pour commencer par 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

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

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();

-1

Si vous utilisez pgAdmin3, développez «Séquences», faites un clic droit sur une séquence, allez dans «Propriétés», et dans l'onglet «Définition» changez «Valeur actuelle» en la valeur que vous voulez. Il n'y a pas besoin de requête.


3
Votre réponse n'ajoute aucune valeur si vous ne nous dites pas au moins quel outil vous utilisez.
11101101b

3
C'est la manière la plus simple, évidemment je pense qu'il dit pg admin 3
MvcCmsJon
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.