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!
You can use a select-statement within an INSERT statement to insert zero, one, or more rows into a table from the result table of the select-statement. The select-statement embedded in the INSERT statement is no different from the select-statement you use to retrieve data.
The INSERT INTO SELECT statement copies data from one table and inserts it into another table.
If you want to add data to your SQL table, then you can use the INSERT statement. Here is the basic syntax for adding rows to your SQL table: INSERT INTO table_name (column1, column2, column3,etc) VALUES (value1, value2, value3, etc); The second line of code is where you will add the values for the rows.
To insert records from multiple tables, use INSERT INTO SELECT statement. Here, we will insert records from 2 tables.
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.
db<>fiddle here
Old sqlfiddle
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With