Reputation: 377
I'm trying to figure out how to insert data from Table1 into Table2, then use the newly-created ID from Table2 to update the corresponding row in Table1.
I'm using Postgres 12.4 for what it's worth
Example:
I've got two tables, e.g. users
and metadata
The users tables has the following columns
| id | info | metadata_id |
The metadata table has the following columns
| id | data |
I want to migrate all of my info
values from the users
table into the data
column of the metadata
table, and update my users.metadata_id
(currently blank) with the corresponding metadata.id
values, essentially backfilling foreign keys.
Is there any way to accomplish this gracefully? I've got a working query which locks both tables and creates a temporary sequence to insert into the metadata.id
and users.metadata_id
but this seems brittle and I would need to start the sequence after the highest-existing ID in the metadata table, which isn't ideal.
I've also tried to use a data-modifying CTE with a RETURNING clause to update the users
table, but couldn't get that to work.
Upvotes: 2
Views: 1836
Reputation: 618
A normalized approach would be to put the user_id in the metadata which also solves this problem for saving the id on the related resource (you just insert it with the other user.info field).
I was having this same problem. Initially I opted for a solution where I set a temporary user_id column on metadata to facilitate putting the metadata id on users but ultimately realized a more normalized approach would generally be better for me (as well as sidestepping this issue). In particular, if there is no metadata you have to store it as null. It's workable but ultimately I found it simpler to have user_id on metadata.
Upvotes: 0
Reputation: 222632
You can't use returning
here, since you need to keep track of the association of users and metadata while inserting.
I think it is simpler to first pre-generate the metadata serial of each user in a CTE, using nextval()
. You can then use that information to insert into metadata and update the users table:
with
candidates as (
select u.*, nextval(pg_get_serial_sequence('metadata', 'id')) new_metadata_id
from users u
),
inserted as (
insert into metadata (id, data) overriding system value
select new_metadata_id, info from candidates
)
update users u
set metadata_id = c.new_metadata_id
from candidates c
where c.id = u.id
We need the overriding system value
clause in the insert
statement so Postgres allows us to write to a serial
column.
Upvotes: 6