Reputation: 13
I have a problem with a really big database with following scheme:
id | date | other columns...
The id
column is from type integer
. It would be ideal if it where from type integer
with a nextval
constraint. Many of the id
entries have unique id
's incremented when they where added.
The problem is all rows added since a specific date
have no id
and the value is null
.
Is it possible to add such constraints to tables with existing values (plus null
values) so that the null
values are filled with integer id
's?
And is this possible without losing the old id
's and in the best case with ascending order in relation to the date column?
thanks and greetings
Upvotes: 1
Views: 2846
Reputation:
You need to first update the existing rows with a unique, non-null value:
update the_table
set id = new_id
from (
select ctid,
(select max(id) from the_table) + row_number() over (order by date) as new_id
from the_table
where id is null
) t
where t.ctid = the_table.ctid;
I am not sure if the order of the IDs is guaranteed using this approach, but it's likely that it does.
Now, that the column doesn't contain any NULL values, we can either change it automatically assign new values.
The next steps depend on whether you want to make this an identity
column or simply a column with a default from a sequence (essentially a (discouraged) serial
column)
We need to create a sequence and sync it with the highest value in the column.
create sequence the_table_id_seq;
select setval('the_table_id_seq', max(id))
from the_table;
Then use this for the default and link the sequence to the column.
alter table the_table
alter id set not null,
alter id set default nextval('the_table_id_seq') ;
alter sequence the_table_id_seq owned by the_table.id;
identity
column (recommended)To make this a proper (recommended) identity
column (Postgres 10 and later) you can do it like this:
alter table the_table
alter id set not null,
alter id add generated always as identity;
Now adding the identity attribute created a new sequence which we need to sync with the existing values in the column:
select setval(pg_get_serial_sequence('the_table', 'id'), max(id))
from the_table;
Alternatively, you could have manually looked up the current max value and provide that directly when specifying the identity
default:
alter table the_table
alter id set not null,
alter id add generated always as identity (start with 42);
Upvotes: 4