admin管理员组文章数量:1122832
I want to move data from several similarly structured tables like old_metadata
to table new_metadata
and update references in tables respectively. For example deposit
has field metadata_id
that references old_metadata.id
. Trick is I don't know id's of records in new_metadata
tables until they are created;
Table structures are like:
old_metadata
id | amount
---+-------
0 | 30
1 | 40
new_metadata
id | amount | metadata_type
---+--------+--------------
31 | 30 | OLD_METADATA
32 | 40 | OLD_METADATA
deposits
id | metadata_id | ...
---+-------------+----
11 | 1 |
12 | 2 |
So what i've tried was
ALTER TABLE deposits ADD COLUMN new_metadata_id INT REFERENCES new_metadata(id);
WITH metadata AS (
INSERT INTO new_metadata (amount, metadata_type)
SELECT amount, 'OLD_METADATA' FROM old_metadata
RETURNING id, old_metadata.id as old_id
)
UPDATE deposits SET new_metadata_id = metadata.id
FROM metada
WHERE metadata_id = metadata.old_id;
My next steps would be replace data in old column with new but it fails saying missing FROM-clause entry for table "old_metadata"
.
Alternatively may be it can be done with cursors or temp tables but I believe it's not the most straightforward way.
Sloppy but working solution would be something like
ALTER TABLE new_metadata ADD COLUMN old_id INT;
ALTER TABLE deposits ADD COLUMN new_metadata_id INT;
WITH metadata AS (
INSERT INTO new_metadata (amount, metadata_type, old_id)
SELECT amount, 'OLD_METADATA', id FROM old_metadata
RETURNING *
)
UPDATE deposits SET new_metadata_id = metadata.id
FROM new_metadata, old_metadata
WHERE metadata_id = new_metadata.old_id;
UPDATE deposits SET metadata_id = deposits.new_metadata_id
ALTER TABLE deposits DROP COLUMN new_metadata_id
ALTER TABLE new_metadata DROP COLUMN old_id
I want to move data from several similarly structured tables like old_metadata
to table new_metadata
and update references in tables respectively. For example deposit
has field metadata_id
that references old_metadata.id
. Trick is I don't know id's of records in new_metadata
tables until they are created;
Table structures are like:
old_metadata
id | amount
---+-------
0 | 30
1 | 40
new_metadata
id | amount | metadata_type
---+--------+--------------
31 | 30 | OLD_METADATA
32 | 40 | OLD_METADATA
deposits
id | metadata_id | ...
---+-------------+----
11 | 1 |
12 | 2 |
So what i've tried was
ALTER TABLE deposits ADD COLUMN new_metadata_id INT REFERENCES new_metadata(id);
WITH metadata AS (
INSERT INTO new_metadata (amount, metadata_type)
SELECT amount, 'OLD_METADATA' FROM old_metadata
RETURNING id, old_metadata.id as old_id
)
UPDATE deposits SET new_metadata_id = metadata.id
FROM metada
WHERE metadata_id = metadata.old_id;
My next steps would be replace data in old column with new but it fails saying missing FROM-clause entry for table "old_metadata"
.
Alternatively may be it can be done with cursors or temp tables but I believe it's not the most straightforward way.
Sloppy but working solution would be something like
ALTER TABLE new_metadata ADD COLUMN old_id INT;
ALTER TABLE deposits ADD COLUMN new_metadata_id INT;
WITH metadata AS (
INSERT INTO new_metadata (amount, metadata_type, old_id)
SELECT amount, 'OLD_METADATA', id FROM old_metadata
RETURNING *
)
UPDATE deposits SET new_metadata_id = metadata.id
FROM new_metadata, old_metadata
WHERE metadata_id = new_metadata.old_id;
UPDATE deposits SET metadata_id = deposits.new_metadata_id
ALTER TABLE deposits DROP COLUMN new_metadata_id
ALTER TABLE new_metadata DROP COLUMN old_id
Share
Improve this question
edited Nov 22, 2024 at 16:55
Ahnassi
asked Nov 22, 2024 at 15:39
AhnassiAhnassi
759 bronze badges
6
|
Show 1 more comment
1 Answer
Reset to default 0See example
Data model for test:
create table old_metadata ( id int primary key generated by default as identity, amount int);
insert into old_metadata values
(0, 30)
,(1, 40)
,(2, 50)
;
create table new_metadata ( id int primary key generated by default as identity
, amount int,metadata_type varchar(20));
insert into new_metadata values
(31 , 30, 'OLD_METADATA')
,(32 , 40, 'OLD_METADATA')
;
create table deposits ( id int primary key generated by default as identity
, metadata_id int );
alter table deposits add constraint
cx_reference_fk_old_metadata FOREIGN KEY (metadata_id) references old_metadata;
insert into deposits values
(11 , 1)
,(12 , 2)
;
CREATE TABLE, INSERT 0 3, CREATE TABLE, INSERT 0 2, CREATE TABLE, ALTER TABLE, INSERT 0 2
old_metadata
id | amount |
---|---|
0 | 30 |
1 | 40 |
2 | 50 |
new_metadata
id | amount | metadata_type |
---|---|---|
31 | 30 | OLD_METADATA |
32 | 40 | OLD_METADATA |
deposits
id | metadata_id |
---|---|
11 | 1 |
12 | 2 |
Add column old_metadata_id to table new_metadata. This column can be deleted later.
ALTER TABLE new_metadata ADD COLUMN old_metadata_id INT REFERENCES old_metadata(id);
We will insert into new_metadata values from old_metadata with new Id. For simplicity, let's make sure that the next Id does not overlap with those already present in new_metadata. We set next Id to max(id)+1=32+1=33.
SELECT setval('new_metadata_id_seq', max(id)) FROM new_metadata;
setval |
---|
32 |
Drop reference to old_metadata.
ALTER TABLE deposits DROP CONSTRAINT cx_reference_fk_old_metadata;
WITH metadata AS (
INSERT INTO new_metadata (amount, metadata_type,old_metadata_id)
SELECT amount, 'OLD_METADATA' metadata_type,id as old_metadata_id FROM old_metadata
RETURNING id, old_metadata_id
)
UPDATE deposits SET metadata_id = metadata.id
FROM metadata
WHERE metadata_id = old_metadata_id;
UPDATE 2
Set reference to new_metadata
alter table deposits add constraint
cx_reference_fk_new_metadata FOREIGN KEY (metadata_id) references new_metadata;
After UPDATE
old_metadata
id | amount |
---|---|
0 | 30 |
1 | 40 |
2 | 50 |
new_metadata
id | amount | metadata_type | old_metadata_id |
---|---|---|---|
31 | 30 | OLD_METADATA | null |
32 | 40 | OLD_METADATA | null |
33 | 30 | OLD_METADATA | 0 |
34 | 40 | OLD_METADATA | 1 |
35 | 50 | OLD_METADATA | 2 |
deposits
id | metadata_id |
---|---|
11 | 34 |
12 | 35 |
fiddle
For question can this part cause collision when newely set metadata_id has the id that is in old_metadata_id variables?
UPDATE deposits SET metadata_id = metadata.id FROM metadata WHERE metadata_id = old_metadata_id;
If you have one old_metadata table, then the intersection of new_metadata_id and old_metadata_id does not cause problems.
If you have several old_metadata tables, then this problem may arise if you consistently process these tables in the specified way.
In this case, it is better to collect all old_metadata in one table and then apply the update 1 time.
Or calculate max(old_metadata_id) in all old_metadata tables and set this value as nextval for new_metadata_id. In this case new_metadata_id will be greater than all old_metadata_id.
If you have intersections by old_metadata_id in the old_metadata tables, then you first need to resolve this collision in some way. This is no longer relevant to your question.
SELECT * FROM pg_sequences WHERE sequencename LIKE '%id%';
schemaname | sequencename | sequenceowner | data_type | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value |
---|---|---|---|---|---|---|---|---|---|---|
public | old_metadata_id_seq | postgres | integer | 1 | 1 | 2147483647 | 1 | f | 1 | null |
public | new_metadata_id_seq | postgres | integer | 1 | 1 | 2147483647 | 1 | f | 1 | 35 |
public | deposits_id_seq | postgres | integer | 1 | 1 | 2147483647 | 1 | f | 1 | null |
版权声明:本文标题:postgresql - Move SQL data from one table to another with preserving foreign keys in third - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736302643a1931610.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
INSERT...RETURNING
syntax. – Bill Karwin Commented Nov 22, 2024 at 15:50ALTER TABLE old_meta ADD COLUMN metadata_type varchar
– Adrian Klaver Commented Nov 22, 2024 at 16:09old_metadata
there are like 10 similaranother_old_metadata
tables with exact same problem. – Ahnassi Commented Nov 22, 2024 at 16:15