Data migration: copy data with insert select and fill unique column
-
02-03-2021 - |
Question
I have some tables in Postgres like this.
create table foo (
txt text
);
insert into foo (txt) values ('a');
insert into foo (txt) values ('b');
insert into foo (txt) values ('c');
create table bar (
txt text,
n integer not null unique
);
I'm doing a schema migration. I would like to migrate data from foo
to bar
. I'd like bar
to look like this.
=# select * from new;
txt | n
----+--
a | 1
b | 2
c | 3
n
doesn't have to be sequential, but it does have to be unique
and not null
.
I thought this query would work.
insert into bar (txt, n)
select txt, generate_series(1, (select count(*) from foo))
from foo;
But I think that's generating rows 1-N over and over again for each 'a', 'b', 'c'
, which violates the unique
constraint.
SQL Fiddle: http://sqlfiddle.com/#!17/06e92c
Update
Okay, I found one hacky way to do it.
insert into bar (txt, n)
select txt, trunc(random() * 999999 + 1)
from foo;
Of course this isn't guaranteed to be unique
. :/ But, if the data set is small enough, you might be able to scrape by with this.
Also, I understand why the first approach failed now. generate_series
returns multiple rows, whereas with random
I only return 1 row.
La solution
Why not use row_number()
insert into bar (txt, n)
select txt, row_number() over ()
from foo;
I left out an order by
which would slow down the query and wouldn't really be an advantage in your case as you just want a unique number.
An alternative would be to create a sequence for it, if the performance of the window function isn't sufficient.
create sequence seq_foo_nr;
insert into bar (txt, n)
select txt, nextval('seq_foo_nr')
from foo;
drop sequence seq_foo_nr;
If you need an auto-generated number anyway, then define nr
as an identity column:
create table bar (
txt text,
n integer not null unique generated always as identity
);
insert into bar (txt)
select txt
from foo;