Comment ajouter une clé primaire auto-incrémentée à une table existante, dans PostgreSQL?

190

J'ai une table avec des données existantes. Existe-t-il un moyen d'ajouter une clé primaire sans supprimer ni recréer la table?

xRobot
la source

Réponses:

355

( Mise à jour - Merci aux personnes qui ont commenté )

Versions modernes de PostgreSQL

Supposons que vous ayez une table nommée test1, à laquelle vous souhaitez ajouter une idcolonne de clé primaire (substitut) à incrémentation automatique . La commande suivante devrait être suffisante dans les versions récentes de PostgreSQL:

   ALTER TABLE test1 ADD COLUMN id SERIAL PRIMARY KEY;

Anciennes versions de PostgreSQL

Dans les anciennes versions de PostgreSQL (antérieures à 8.x?), Vous deviez faire tout le sale boulot. La séquence de commandes suivante devrait faire l'affaire:

  ALTER TABLE test1 ADD COLUMN id INTEGER;
  CREATE SEQUENCE test_id_seq OWNED BY test1.id;
  ALTER TABLE test ALTER COLUMN id SET DEFAULT nextval('test_id_seq');
  UPDATE test1 SET id = nextval('test_id_seq');

Encore une fois, dans les versions récentes de Postgres, cela équivaut à peu près à la commande unique ci-dessus.

leonbloy
la source
3
J'utilise ORACLE, donc le partager peut être utile pour les gars ORACLE Dans ORACLE: ALTER TABLE TEST1 ADD ID NUMBER; UPDATE TEST1 SET ID = TEST1_SEQ.NEXTVAL; ALTER TABLE TEST1 AJOUTER LA CLÉ PRIMAIRE (ID); créer une séquence TEST1_SEQ avant d'exécuter l'instruction UPDATE
msbyuva
Notez que ADD PRIMARY KEYcrée également une NOT NULLcontrainte (testée dans postgres 9.3) comme prévu et souhaité.
Jared Beck
19
Dans Postgres, vous pouvez utiliser une seule commandeALTER TABLE test1 ADD COLUMN id SERIAL PRIMARY KEY;
resnyanskiy
1
Suite au commentaire de @ resnyanskiy, cela fonctionnera même s'il y a des données dans le tableau. Les ID sont renseignés et une contrainte non nulle est définie. La réponse entière peut être remplacée par la ligne dans ce commentaire.
Synesso
1
@EricWang Merci, Eric, vous avez raison - je crois que cela n'a pas fonctionné il y a certaines versions (années), mais je ne suis pas sûr. Transformé la réponse en communauté-wiki.
leonbloy
57
ALTER TABLE test1 ADD COLUMN id SERIAL PRIMARY KEY;

C'est tout ce dont vous avez besoin pour:

  1. Ajouter la idcolonne
  2. Remplissez-le avec une séquence de 1 à count (*).
  3. Définissez-le comme clé primaire / non nul.

Le crédit est donné à @resnyanskiy qui a donné cette réponse dans un commentaire.

Synesso
la source
2
Cela devrait être marqué comme réponse, et la réponse devrait appartenir à @resnyanskiy
Eric Wang
Je devais d'abord laisser tomber la clé p, puis l'exécuter. ALTER TABLE <table> DROP CONSTRAINT <pkey_name>;
Josh Robertson
10

Pour utiliser une colonne d'identité dans la v10,

ALTER TABLE test 
ADD COLUMN id { int | bigint | smallint}
GENERATED { BY DEFAULT | ALWAYS } AS IDENTITY PRIMARY KEY;

Pour une explication des colonnes d'identité, voir https://blog.2ndquadrant.com/postgresql-10-identity-columns/ .

Pour connaître la différence entre GENERATED BY DEFAULT et GENERATED ALWAYS, voir https://www.cybertec-postgresql.com/en/sequences-gains-and-pitfalls/ .

Pour modifier la séquence, voir https://popsql.io/learn-sql/postgresql/how-to-alter-sequence-in-postgresql/ .

jhoanna
la source
Le problème avec cette solution est que si le tableau contient déjà des lignes, vous obtenez une erreur:SQL Error [23502]: ERROR: column "id" contains null values
isapir
3
@isapir: Il y avait un bogue dans les premières versions (pg 10 et 10.1) produisant cette erreur. Il a été corrigé avec la page 10.2. Détails ici: dba.stackexchange.com/q/200143/3684
Erwin Brandstetter
Merci @ erwin-brandstetter
isapir
Un an plus tard, j'ai retrouvé cette réponse, bug apparemment corrigé, voté pour;)
isapir
2

J'ai atterri ici parce que je cherchais aussi quelque chose comme ça. Dans mon cas, je copiais les données d'un ensemble de tables intermédiaires avec de nombreuses colonnes dans une table tout en affectant également des identifiants de ligne à la table cible. Voici une variante des approches ci-dessus que j'ai utilisées. J'ai ajouté la colonne série à la fin de ma table cible. De cette façon, je n'ai pas besoin d'avoir un espace réservé pour cela dans l'instruction Insert. Ensuite, une simple sélection * dans la table cible a automatiquement rempli cette colonne. Voici les deux instructions SQL que j'ai utilisées sur PostgreSQL 9.6.4.

ALTER TABLE target ADD COLUMN some_column SERIAL;
INSERT INTO target SELECT * from source;
Dean Sha
la source