286

I have a table with existing data. Is there a way to add a primary key without deleting and re-creating the table?

xRobot
  • 25,579
  • 69
  • 184
  • 304

5 Answers5

528

(Updated - Thanks to the people who commented)

Modern Versions of PostgreSQL

Suppose you have a table named test1, to which you want to add an auto-incrementing, primary-key id (surrogate) column. The following command should be sufficient in recent versions of PostgreSQL:

   ALTER TABLE test1 ADD COLUMN id SERIAL PRIMARY KEY;

Older Versions of PostgreSQL

In old versions of PostgreSQL (prior to 8.x?) you had to do all the dirty work. The following sequence of commands should do the trick:

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

Again, in recent versions of Postgres this is roughly equivalent to the single command above.

Jason Slobotski
  • 1,386
  • 14
  • 18
leonbloy
  • 73,180
  • 20
  • 142
  • 190
  • 4
    I am using ORACLE, so sharing it might be useful for ORACLE guys In ORACLE : ALTER TABLE TEST1 ADD ID NUMBER; UPDATE TEST1 SET ID = TEST1_SEQ.NEXTVAL; ALTER TABLE TEST1 ADD PRIMARY KEY(ID); create a Sequence TEST1_SEQ before executing UPDATE statement – msbyuva Jun 22 '11 at 20:19
  • Note that `ADD PRIMARY KEY` also creates a `NOT NULL` constraint (tested in postgres 9.3) as expected and wanted. – Jared Beck Feb 07 '14 at 20:36
  • 1
    Further to @resnyanskiy's comment, this will work even when there's data in the table. IDs are populated and not null constraint is set. The entire answer can be replaced by the line in that comment. – Synesso May 04 '15 at 00:32
  • You don't need the 2nd line at all, actually it's bad to add the 2nd line, because after the first line, the id column is added, and all existing rows already have an auto increased id, you u execute the 2nd line again, then all id will be changed re-base start from N+1, where N is count of existing rows. – Eric May 16 '16 at 09:45
  • 1
    @EricWang Thanks, Eric, you're right - I believe that this didn't work some versions (years) ago, but I'm not sure. Turned the answer into community-wiki. – leonbloy May 16 '16 at 14:10
  • is it possible to make primary key(serial) without adding new column – satish kilari Aug 03 '16 at 13:42
  • alter column datatype ! – Rizwan Patel May 17 '17 at 10:56
  • The doc history for Postgres suggests the SERIAL datatype was added in 7.2: https://www.postgresql.org/docs/7.1/datatype.html vs. https://www.postgresql.org/docs/7.2/datatype.html. 7.2 was released in 2002, so it seems safe to assume most modern Postgres installations will support it. – Tim Keating Nov 16 '18 at 18:31
  • It seems that for compatibility and less problem with permission, is better to use the "IDENTITY" type from Postgress 10+ Check https://blog.2ndquadrant.com/postgresql-10-identity-columns/ – Antimo Jan 11 '19 at 16:45
  • Do we know how postgres generate SEQUENCE to existing table? Example order by created_at or id? – Uysim Ty Mar 12 '19 at 03:39
  • 1
    What about large tables? Do these statements lock for a long time? – Ezequiel Aug 29 '19 at 10:26
  • 4
    When using the `ALTER TABLE ADD COLUMN id SERIAL PRIMARY KEY`, how can you specify the `id` to be `bigint`?
    – p.matsinopoulos Jun 04 '20 at 07:49
  • 4
    @p.matsinopoulos Just replace SERIAL -> BIGSERIAL – Israel Lins Albuquerque Oct 22 '20 at 11:20
  • Using this, is it possible to specify the order in which the new primary key ids are assigned? For example, based on the sort order of a specific column? – Sirach Matthews Jan 26 '21 at 15:16
  • 1
    @SirachMatthews See here https://stackoverflow.com/questions/53370072/add-auto-increment-column-to-existing-table-ordered-by-date – leonbloy Jan 26 '21 at 18:28
  • How can I do the same thing but with an existing primary key column? If a primary key constraint was added but forgot to make it auto-increment – MoKG Jan 30 '23 at 10:22
  • Found an answer. `ALTER TABLE ALTER column ADD GENERATED BY DEFAULT AS IDENTITY;`. https://dba.stackexchange.com/questions/285869/add-auto-increment-to-already-existing-primary-key-column-postgresql – MoKG Jan 30 '23 at 10:34
78
ALTER TABLE test1 ADD COLUMN id SERIAL PRIMARY KEY;

This is all you need to:

  1. Add the id column
  2. Populate it with a sequence from 1 to count(*).
  3. Set it as primary key / not null.

Credit is given to @resnyanskiy who gave this answer in a comment.

Synesso
  • 37,610
  • 35
  • 136
  • 207
19

To use an identity column in v10,

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

For an explanation of identity columns, see https://blog.2ndquadrant.com/postgresql-10-identity-columns/.

For the difference between GENERATED BY DEFAULT and GENERATED ALWAYS, see https://www.cybertec-postgresql.com/en/sequences-gains-and-pitfalls/.

For altering the sequence, see https://popsql.io/learn-sql/postgresql/how-to-alter-sequence-in-postgresql/.

jhoanna
  • 1,797
  • 25
  • 25
  • The problem with this solution is that if the table already contains rows then you get an error: `SQL Error [23502]: ERROR: column "id" contains null values` – isapir Mar 07 '18 at 16:30
  • 3
    @isapir: There was a bug in early versions (pg 10 and 10.1) producing this error. It was fixed with pg 10.2. Details here: https://dba.stackexchange.com/q/200143/3684 – Erwin Brandstetter May 22 '18 at 01:04
7

I landed here because I was looking for something like that too. In my case, I was copying the data from a set of staging tables with many columns into one table while also assigning row ids to the target table. Here is a variant of the above approaches that I used. I added the serial column at the end of my target table. That way I don't have to have a placeholder for it in the Insert statement. Then a simple select * into the target table auto populated this column. Here are the two SQL statements that I used on PostgreSQL 9.6.4.

ALTER TABLE target ADD COLUMN some_column SERIAL;
INSERT INTO target SELECT * from source;
Dean Sha
  • 837
  • 1
  • 10
  • 15
0

ALTER TABLE test1 ADD id int8 NOT NULL GENERATED ALWAYS AS IDENTITY;

yaseer
  • 21
  • 4
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 18 '21 at 06:39
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/30105672) – Smit Gajera Oct 18 '21 at 07:10