patternsqlMinor
Is there a way to clone a row in a "maintainable" way (with PostgreSQL)?
Viewed 0 times
postgresqlwithmaintainablewayrowthereclone
Problem
I often have to clone rows in my applications so I do it the long way :
(id is declared as a
The thing is, when I add new fields to
I would like to clone rows in a more maintainable way. Something like :
As I write it, I also think it would be nice to clone detail rows : if I have a detail/child table declared with referential integrity like this :
I could clone mytable and all childs in one command rather than :
Is there anything in PostgreSQL (or extensions) that could help dealing with these cases ?
INSERT INTO mytable (a, b, c)
SELECT a,b,c FROM mytable WHERE id=12;(id is declared as a
SERIAL PRIMARY KEY, 12 being the row to clone).The thing is, when I add new fields to
mytable I must remember to update the clone functions to also clone those new fields.I would like to clone rows in a more maintainable way. Something like :
INSERT INTO mytable
SELECT *, id:DEFAULT FROM mytable WHERE id=12;As I write it, I also think it would be nice to clone detail rows : if I have a detail/child table declared with referential integrity like this :
CREATE TABLE mychild (
id serial PRIMARY KEY,
parentid int REFERENCES mytable(id),
a text
);I could clone mytable and all childs in one command rather than :
- clone mytable (the long way)
- get the new
id
- clone all
mychild, settingparentidto the newid
Is there anything in PostgreSQL (or extensions) that could help dealing with these cases ?
Solution
With the additional module
All columns are cast to
Closely related answer with detailed explanation and more (possibly a bit faster) alternatives:
Clone parent row and all its children
That's more tricky. But based on the above, plus using a data-modifying CTE, there is a very elegant solution:
This uses the new
hstore installed, there is a very simple way to replace the value(s) of individual field(s) without knowing anything about other columns:INSERT INTO mytable
SELECT (t1).* -- note the parentheses
FROM (
SELECT t #= hstore('id', nextval(pg_get_serial_sequence('mytable', 'id'))::text) AS t1
FROM mytable t
WHERE id = 12
) sub;All columns are cast to
text and back in the process.Closely related answer with detailed explanation and more (possibly a bit faster) alternatives:
- Duplicate row with Primary Key in PostgreSQL
Clone parent row and all its children
That's more tricky. But based on the above, plus using a data-modifying CTE, there is a very elegant solution:
WITH ins_parent AS (
INSERT INTO mytable
SELECT (p).* -- note the parentheses
FROM (
SELECT t #= hstore('id', nextval(pg_get_serial_sequence('mytable', 'id'))::text) AS p
FROM mytable t
WHERE id = 2
) sub
RETURNING id
)
INSERT INTO mychild
SELECT (c).*
FROM (
SELECT t #= hstore('id', nextval(pg_get_serial_sequence('mychild', 'id'))::text)
#= hstore('parentid', (SELECT id::text FROM ins_parent)) AS c
FROM mychild t
WHERE parentid = 2
) sub;This uses the new
parentid for all copies in the child table and assigns new serial values to the id column.Code Snippets
INSERT INTO mytable
SELECT (t1).* -- note the parentheses
FROM (
SELECT t #= hstore('id', nextval(pg_get_serial_sequence('mytable', 'id'))::text) AS t1
FROM mytable t
WHERE id = 12
) sub;WITH ins_parent AS (
INSERT INTO mytable
SELECT (p).* -- note the parentheses
FROM (
SELECT t #= hstore('id', nextval(pg_get_serial_sequence('mytable', 'id'))::text) AS p
FROM mytable t
WHERE id = 2
) sub
RETURNING id
)
INSERT INTO mychild
SELECT (c).*
FROM (
SELECT t #= hstore('id', nextval(pg_get_serial_sequence('mychild', 'id'))::text)
#= hstore('parentid', (SELECT id::text FROM ins_parent)) AS c
FROM mychild t
WHERE parentid = 2
) sub;Context
StackExchange Database Administrators Q#148908, answer score: 3
Revisions (0)
No revisions yet.