【问题标题】:Duplicate datas across tables keeping referencies IDs跨表重复数据保留引用 ID
【发布时间】:2015-08-25 16:48:49
【问题描述】:

我正在征求您的意见以了解这是否可能,因为我是菜鸟,也许我正试图以完全错误的方式做到这一点。我进行了很多搜索,但一无所获,可能没有使用正确的搜索条件。
我正在使用 Postgresql 9.1。我已经简化了情况来理解方法

我有 3 张桌子:

  • 表 A(主)
  • 表 B(链接到表 A)
  • 表 C(链接到表 B)

(查看架构)

表 A

    id_A | data_A
    -----+--------
     A01 | dataA01
     A02 | dataA02

表 B

    id_B | ref_id_A | data_B
    -----+----------+--------
     B01 |    A01   | dataB01
     B02 |    A01   | dataB02
     B03 |    A02   | dataB03
     B04 |    A02   | dataB04
     B05 |    A02   | dataB05
     B06 |    A02   | dataB06

表 C

    id_C | ref_id_B | data_C
    -----+----------+--------
     C01 |    B01   | dataC01
     C02 |    B01   | dataC02
     C03 |    B02   | dataC03
     C04 |    B02   | dataC04

我要做的是复制链接到表 A 的所有数据,并使用新 ID 保留存储在表 B 和 C 中的所有数据和引用。它应该像复制和粘贴一样工作,以便在开发新版本的同时维护旧版本

复制 A01 -> A03

表 A

    id_A | data_A
    -----+--------
     A01 | dataA01
     A02 | dataA02
     A03 | dataA01

表 B

    id_B | ref_id_A | data_B
    -----+----------+--------
     B01 |    A01   | dataB01
     B02 |    A01   | dataB02
     B03 |    A02   | dataB03
     B04 |    A02   | dataB04
     B05 |    A02   | dataB05
     B06 |    A02   | dataB06
     B07 |    A03   | dataB01
     B08 |    A03   | dataB02

表 C

    id_C | ref_id_B | data_C
    -----+----------+--------
     C01 |    B01   | dataC01
     C02 |    B01   | dataC02
     C03 |    B02   | dataC03
     C04 |    B02   | dataC04
     C05 |    B07   | dataC01
     C06 |    B07   | dataC02
     C07 |    B08   | dataC03
     C08 |    B08   | dataC04  

我编写了这个查询来复制 A01,它完美地保存了表 B 中的数据,但我无法复制表 C 中的数据

WITH new_id_A as 
(insert into table_A (id_A, data_A) (select id_A, data_A from table_A where id_A='A01') returning id_A) 
 insert into table_B (ref_id_A, data_B) select (select id_A from new_id_A) as ref_id_A, data_B from table_B where ref_id_A='A01';

我尝试了不同的方法,但没有结果
我可以使用代码轻松完成任务,但我更愿意在单个事务中在数据库中完成它
感谢您的关注

【问题讨论】:

  • 如何在表中生成 ID?理想情况下,这将按顺序完成。在这种情况下,插入数据的“副本”应该不难。
  • Table A 发生更改时,您可以编写一个触发器以使用新ID 更新Table B
  • @Bulat ID 是串行自动增量的,无法在不更改所有表的情况下更改表结构来存储字母数字 ID。但如果我要创建一个新数据库是个好主意
  • @Colin 我不太明白你关于触发器的建议。你是说我应该触发一个触发器,当我在表 A 中添加一个新行时它应该复制一条记录?哪个记录?如何指定哪个记录?也许我需要一个函数
  • 使用我之前写的查询的方法,问题是我可以报告表B的新ID,但是表C中的数据与旧ID_B相关。所以我需要一个带有 old_id_1 -> new_id_1 的表来从表 C 中检索数据

标签: sql database postgresql postgresql-9.1


【解决方案1】:

为了简单起见,我认为您应该像这样为临时表中的新行暂存 ID:

WITH new_id_A as 
(insert into table_A (id_A, data_A) (select id_A, data_A from table_A where id_A='A01') returning id_A) 
SELECT 'A01' AS old_id, id_A AS new_id
INSERT INTO TEMP table_A_copy;

insert into table_B (ref_id_A, data_B) 
select cp.new_id, b.data_B
from 
 table_A_copy cp INNER JOIN
 table_B b ON b.ref_id_A = cp.old_id;

insert into table_C (ref_id_B, data_B) 
select b_new.id_B, c.data_C 
from 
 table_A_copy cp INNER JOIN
 table_b b_old on b_old.ref_id_a = cp.old_id inner join 
 table_b b_new on b_new.ref_id_a = cp.new_id inner join 
 table_C c ON c.ref_id_B = b_old.id_B;

法比奥的更新:

drop table if exists table_a_copy; 
create temp table table_a_copy ("old_id" integer, "new_id" integer); 

with new_id_a as 
(insert into table_a (data_a) 
  (select data_a from table_a where id_a='1') returning id_a) 
 insert into table_a_copy select '1'::integer as old_id, 
  (select * from new_id_a) as new_id; 

-- Insert to table B
insert into table_b (ref_id_a, data_b) 
select cp.new_id, b.data_b 
from 
  table_a_copy cp inner join 
  table_b b on b.ref_id_a = cp.old_id;

-- Insert to table C
insert into table_c (ref_id_B, data_C) 
select b_new.id_b, c.data_c 
from 
  table_a_copy cp inner join 
  table_b b_old on b_old.ref_id_a = cp.old_id inner join 
  table_b b_new on b_new.ref_id_a = cp.new_id inner join 
  table_c c on c.ref_id_b = b_old.id_b;

【讨论】:

  • 试过你的代码,但不工作,从选择中制作临时表的问题。因此,将代码更改为在临时表之前构建,然后将数据放入其中。最后一行查询table_C c ON b.ref_id_C = c.id_C 也有问题,在我看来应该是table_C c ON c.ref_id_B = b.id_B。这样查询就可以了,但是table_C中复制的数据没有正确引用到table_B
  • 这里是我使用的查询,为了避免评论字符限制而拆分
  • drop table if exists table_a_copy; create temp table table_a_copy ("old_id" integer, "new_id" integer); with new_id_a as (insert into table_a (data_a) (select data_a from table_a where id_a='1') returning id_a) insert into table_a_copy select '1'::integer as old_id, (select * from new_id_a) as new_id; insert into table_b (ref_id_a, data_b) select cp.new_id, b.data_b from table_a_copy cp inner join table_b b on b.ref_id_a = cp.old_id;
  • insert into table_c (ref_id_b, data_c) select b.id_b, c.data_c from table_a_copy cp inner join table_b b on b.ref_id_a = cp.old_id inner join table_c c on c.ref_id_b = b.id_b;
  • @FabioSoggia 抱歉,上次查询中有错字。它最终对你有用吗?
【解决方案2】:

这是我做完测试后的最后一个问题。在我看来工作完美,但我认为应该用更简单的方法替换它
请添加您的意见或更正,谢谢

drop table if exists table_old_id_a;  
drop table if exists table_new_id_a;  
drop table if exists table_joined_old_new_id_a;  
-- 3 table  
-- 1st with old IDs and an arbitrary column to use to join
-- 2nd with new IDs after insert; you just need the ID to avoid that other use the same ID, insert only minimum data
-- 3rd is a table where you join old and new IDs using the join_column sequence
-- after this you must update table_a with all the data you need, but you keep references in temp table table_joined_old_new_id_a
create temp table table_old_id_a ("join_column" serial, "old_id" integer);
create temp table table_new_id_a ("join_column" serial, "new_id" integer);
create temp table table_joined_old_new_id_a ("old_id" integer, "new_id" integer);
insert into table_old_id_a (old_id) select id_a from table_a where id_a='1';
with new_id_a as
(insert into table_a (data_a) (select 'Copy of ' || data_a from table_a where id_a='1') returning id_a) 
insert into table_new_id_a (new_id) select id_a from new_id_a;
insert into table_joined_old_new_id_a select table_old_id_a.old_id, table_new_id_a.new_id from table_old_id_a, table_new_id_a where table_old_id_a.join_column=table_new_id_a.join_column;
-- then update the table with all the data
with t_update_a as (select data_a, new_id, data_a2 from table_a t_old_id, table_joined_old_new_id_a where t_old_id.id_a=table_joined_old_new_id_a.old_id)
update table_a set 
data_a=(select data_a from t_update_a where table_a.id_a=t_update_a.new_id),
data_a2=(select data_a2 from t_update_a where table_a.id_a=t_update_a.new_id)
where table_a.id_a in (select new_id from table_joined_old_new_id_a);
-- table B data
drop table if exists table_old_id_b;
drop table if exists table_new_id_b;
drop table if exists table_joined_old_new_id_b;
create temp table table_old_id_b ("join_column" serial, "old_id" integer);
create temp table table_new_id_b ("join_column" serial, "new_id" integer);
create temp table table_joined_old_new_id_b ("old_id" integer, "new_id" integer);
insert into table_old_id_b (old_id) select id_b from table_b where ref_id_a in (select old_id from table_old_id_a);
with new_id_b as 
(insert into table_b (data_b) (select 'Copy of ' || data_b from table_b, table_old_id_a where table_old_id_a.old_id=table_b.ref_id_a) returning id_b)
insert into table_new_id_b (new_id) select id_b from new_id_b;
insert into table_joined_old_new_id_b select table_old_id_b.old_id, table_new_id_b.new_id from table_old_id_b, table_new_id_b where table_old_id_b.join_column=table_new_id_b.join_column;
-- then update the table with all the data
with t_update_b as  
(select data_b, new_id, ref_id_a from table_b t_old_id, table_joined_old_new_id_b where t_old_id.id_b=table_joined_old_new_id_b.old_id)
update table_b set 
ref_id_a=(select table_joined_old_new_id_a.new_id from table_joined_old_new_id_a, t_update_b where table_joined_old_new_id_a.old_id=t_update_b.ref_id_a and table_b.id_b=t_update_b.new_id),
data_b=(select data_b from t_update_b where table_b.id_b=t_update_b.new_id)
where table_b.id_b in (select new_id from table_joined_old_new_id_b);
-- table C data
drop table if exists table_old_id_c;
drop table if exists table_new_id_c;
drop table if exists table_joined_old_new_id_c;
create temp table table_old_id_c ("join_column" serial, "old_id" integer);
create temp table table_new_id_c ("join_column" serial, "new_id" integer);
create temp table table_joined_old_new_id_c ("old_id" integer, "new_id" integer);
insert into table_old_id_c (old_id) select id_c from table_c where ref_id_b in (select old_id from table_old_id_b);
with new_id_c as 
(insert into table_c (data_c) (select 'Copy of ' || data_c from table_c, table_old_id_b where table_old_id_b.old_id=table_c.ref_id_b) returning id_c)
insert into table_new_id_c (new_id) select id_c from new_id_c;
insert into table_joined_old_new_id_c select table_old_id_c.old_id, table_new_id_c.new_id from table_old_id_c, table_new_id_c where table_old_id_c.join_column=table_new_id_c.join_column;
-- then update the table with all the data
with t_update_c as  
(select data_c, new_id, ref_id_b from table_c t_old_id, table_joined_old_new_id_c where t_old_id.id_c=table_joined_old_new_id_c.old_id)
update table_c set 
ref_id_b=(select table_joined_old_new_id_b.new_id from table_joined_old_new_id_b, t_update_c where table_joined_old_new_id_b.old_id=t_update_c.ref_id_b and table_c.id_c=t_update_c.new_id),
data_c=(select data_c from t_update_c where table_c.id_c=t_update_c.new_id)
where table_c.id_c in (select new_id from table_joined_old_new_id_c);
-- last drop the temp tables
drop table if exists table_old_id_a;
drop table if exists table_new_id_a;
drop table if exists table_joined_old_new_id_a;
drop table if exists table_old_id_b;
drop table if exists table_new_id_b;
drop table if exists table_joined_old_new_id_b;
drop table if exists table_old_id_c;
drop table if exists table_new_id_c;
drop table if exists table_joined_old_new_id_c;
-- Finish!

【讨论】:

  • 在我的测试过程中,我向 table_A 添加了一个列 (data_a2),以便获得更多数据来测试正确的副本,因此它与我发布的初始架构不同
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-08
  • 2014-09-01
  • 2015-03-09
  • 1970-01-01
  • 2016-09-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多