17

I have a database in Postgresql, which was migrated from SQL Server (only data).
On SQL Server, a table from this database has these columns:

measure_id
datum
measure

where measure_id is auto-incremental primary key, datum is datetime and measure is float.
After migration in Postrgresql, measure_id is column of type bigint.

How can I change this column (measure_id) to bigserial and assign it as primary key, now that my table is full of data?

Aaron Bertrand
  • 175,645
  • 25
  • 380
  • 589
zetah
  • 275
  • 1
  • 2
  • 5

2 Answers2

22

Create a sequence and use it as the default value for the column:

create sequence measures_measure_id_seq
   owned by measures.measure_id;

alter table measures
   alter column measure_id set default nextval('measures_measure_id_seq');

commit;

That essentially what serial does.

See the manual for details:
http://www.postgresql.org/docs/current/static/datatype-numeric.html#DATATYPE-SERIAL


Starting with Postgres 10, the recommended way is to use standard compliant identity columns, rather than serial (or bigserial).

alter table measures
   alter measure_id add generated always as identity;

In both cases, you will have to adjust the sequence to match the current maximum value in the measure_id column:

select setval(pg_get_serial_sequence('measures', 'measure_id'), max(measure_id))
from measures;
a_horse_with_no_name
  • 69,148
  • 13
  • 135
  • 173
  • Ok, thanks. Can you just drop a note what does `owned by measures.measure_id` do? – zetah Oct 09 '14 at 06:06
  • @zetah: Quote from the manual (the link I added): "*the sequence is marked as "owned by" the column, so that it will be dropped if the column or table is dropped*" – a_horse_with_no_name Oct 09 '14 at 06:11
  • 2
    Ah, all right it's just like a decoration. I mentioned that table already has data, but newly created sequence wont respect already present data and will start counting from 1, which will error on first new insert for unique constraint. So if anyone else is reading this it's more correct to use `CREATE SEQUENCE seq START WITH x` where x is last row (`SELECT MAX(measure_id)+1 FROM measures` in my case) – zetah Oct 09 '14 at 06:23
  • 1
    @zetah Thanks for this update. How would you write a query in that case? I tried `CREATE SEQUENCE seq START WITH (SELECT MAX(measure_id)+1 FROM measures); ` but this does not seem to be the correct way. – inquisitive Mar 01 '16 at 11:46
  • What you're trying to do is : select setval('sequenceX', (select max(table_id)+1 from table), false) Found here : https://stackoverflow.com/questions/2022559/postgresql-using-subqueries-with-alter-sequence-expressions – HydrUra Apr 26 '19 at 09:10
0

I see that the question is already marked as solved, however, as pointed out in the comments, it is missing the case where there are existing records that already have some values set in the relevant column.

I'll list some other cases as well.

I'm doing this for a table called products which is present in the public schema. The auto-increment is being done for the id column of this table.

1. For a non-existing column

-- auto-increment constraint for a new column
 ALTER TABLE public.products
    ADD COLUMN id SERIAL PRIMARY KEY;

2. For an existing column that got no values in the table

-- create sequence
CREATE SEQUENCE public_products_id_seq OWNED BY public.products.id;

-- use sequence for the target column
ALTER TABLE public.products ALTER COLUMN id SET DEFAULT nextval('public_products_id_seq');

3. For an existing column that already got some values in the table

-- create sequence
CREATE SEQUENCE public_products_id_seq OWNED BY public.products.id;

-- set the current value of the sequence to the max value from that column
-- (id column in this scenario)
SELECT SETVAL('public_products_id_seq', (select max(id) from public.products), false)

-- use sequence for the target column
ALTER TABLE public.products ALTER COLUMN id SET DEFAULT nextval('public_products_id_seq');

Documentation references for CREATE SEQUENCE and SETVAL

Masroor
  • 101
  • 1