INSERT INTO ... FROM SELECT ... RETURNING id mappings
This would be simpler for UPDATE
, where additional rows joined into the update are visible to the RETURNING
clause:
The same is currently not possible for INSERT
. Per documentation:
The expression can use any column names of the table named by table_name
table_name being the target of the INSERT
command.
You can use (data-modifying) CTEs to get this to work.
Assuming title
to be unique per query, else you need to do more:
WITH sel AS ( SELECT id, title FROM posts WHERE id IN (1,2) -- select rows to copy ), ins AS ( INSERT INTO posts (title) SELECT title FROM sel RETURNING id, title )SELECT ins.id, sel.id AS from_idFROM insJOIN sel USING (title);
If title
is not unique per query (but at least id
is unique per table):
WITH sel AS ( SELECT id, title, row_number() OVER (ORDER BY id) AS rn FROM posts WHERE id IN (1,2) -- select rows to copy ORDER BY id ), ins AS ( INSERT INTO posts (title) SELECT title FROM sel ORDER BY id -- ORDER redundant to be sure RETURNING id )SELECT i.id, s.id AS from_idFROM (SELECT id, row_number() OVER (ORDER BY id) AS rn FROM ins) iJOIN sel s USING (rn);
This second query relies on the undocumented implementation detail that rows are inserted in the order provided. It works in all current versions of Postgres and is probably not going to break.
if id
column of posts
is serial
type, it's generated like nextval('posts_id_seq'::regclass)
,you can manually call this function for every new row
withsel as ( SELECT id, title, nextval('posts_id_seq'::regclass) new_id FROM posts WHERE id IN (1,2)),ins as ( INSERT INTO posts (id, title) SELECT new_id, title FROM sel)SELECT id, new_idFROM sel
it'l works with any data, include non-unique title
The simplest solution IMHO would be to simply add a column to your table where you could put id of the row that was cloned.