Soviut
Soviut

Reputation: 91615

How to do an SQL UPDATE based on INSERT RETURNING id in Postgres?

I'm using Postgres. I have an old table and a new table. The records in the old table need to have related records in the new table. The new table records are effectively going to act as the parents for records in the new table.

I'm trying to write a migration where each "child" from the old table will have "parent" in the new table.

OLD TABLE (I've added a foreign key in anticipation of populating it as part of the migration)

 id | name | new_id (FK)
----+------+-------------
  1 | 1st  | 
  2 | 2nd  |

NEW TABLE

 id | name 
----+------
    |

I need to do the following:

Is there a way to accomplish this using a set query? Or do I need to start delving into Postgres specific things like LOOPs?

Upvotes: 6

Views: 9800

Answers (3)

Michel Milezzi
Michel Milezzi

Reputation: 11155

You can use a writable cte as follows:

--open transaction
BEGIN;

--avoid concurrent writes
LOCK old_table IN ROW SHARE MODE;
LOCK new_table IN ROW SHARE MODE;

--create a sequence to populate new_table.id
CREATE SEQUENCE new_table_seq;

--using a writable cte to do the dirty job
WITH old_table_cte AS (
    UPDATE old_table SET new_id = nextval('new_table_seq') RETURNING *
)
INSERT INTO new_table SELECT new_id, name FROM old_table_cte;

--Create a proper FK
ALTER TABLE old_table ADD CONSTRAINT old_table_new_table_fk FOREIGN KEY (new_id) REFERENCES new_table (id);

--end transaction
COMMIT;

This approach has some benefits:

  • Taking advantage of RETURNING as you asked, but in update step. Avoiding an unnecessary extra SELECT;
  • Concurrency safe;
  • Postponing FK creation will result in a faster script;
  • Using pure SQL solution, no need to use procedural language;
  • Avoiding read and write to old_table at same step.

Upvotes: 6

JERRY
JERRY

Reputation: 1173

Use TEMP Table to perform migration. Before update on main table, we should manipulate data over TEMP table as requirement.

Upvotes: -1

Nerdi.org
Nerdi.org

Reputation: 923

This page should help you out :) postgreSQL function for last inserted ID

INSERT INTO tableName (column1, column2) VALUES ('column1val', 'column2val') RETURNING id;

You can then use the returned id to update the original row. You can also use a few other methods suggested on that SO page (mentioned below / linked above)

currval() if you know sequence name, lastval() for last insert in sequence, to alternate between select/insert/update in each loop for original rows, or RETURNING id method.

Upvotes: 0

Related Questions