I'm using PostgreSQL 9.3.
I want to duplicate some of the db records. Since I'm using an auto-increment pk id for the table, I want to get back the id mappings from the generated ids of duplicated records to the original ones. For example, say I have a table posts
with 2 records in it:
[{'id': 1, 'title': 'first'}
, {'id': 2. 'title': 'second'}]
With SQL:
INSERT INTO posts (title) SELECT title FROM posts RETURNING id, ??
I expect to see mappings like:
[{'id': 3, 'from_id': 1}
, {'id': 4, 'from_id': 2}]
Any idea on how to fill in the question marks above to make it work? Thanks a lot!
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
. The manual:
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_id
FROM ins
JOIN 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_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS rn FROM ins) i
JOIN 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.