微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

根据冲突的键在 PostgreSQL 数据库之间移动数据

如何解决根据冲突的键在 PostgreSQL 数据库之间移动数据

情况

我有 2 个数据库,它们曾经是彼此的直接副本,但现在它们包含新的不同数据。

我想做什么

我想将数据从数据库“SOURCE”移动到数据库“TARGET”,但问题是表使用自动递增的键,而且由于两个数据库同时使用,很多 ID 已经被占用在 TARGET 中,所以我不能只是标识插入来自 SOURCE 的数据。

但理论上,我们根本不能使用身份插入,而让数据库负责分配新的 ID。

更难的是我们有大约 50 个表,每个表都通过外键连接。显然,外键也必须更改,否则它们将不再引用正确的内容

让我们看一个非常简单的例子:

table Human {
  id integer NOT NULL PK AutoIncremented
  name varchar NOT NULL
  parentId integer NULL FK -> Human.id 
}

table Pet {
  id integer NOT NULL PK AutoIncremented
  name varchar NOT NULL
  ownerId integer NOT NULL FK -> Human.id 
}

SOURCE Human
Id      name      parentId
==========================
1       Aron      null
2       Bert      1
3       Anna      2

SOURCE Pet
Id      name      ownerId
==========================
1       Frankie   1
2       Doggo     2    

TARGET Human
Id      name      parentId
==========================
1       Armin      null
2       Cecil     1

TARGET Pet
Id      name      ownerId
==========================
1       Gatto     2 

假设我想将 Aron、Bert、Anna、Frankie 和 Doggo 移至 TARGET 数据库

但是如果我们不关心原始id直接尝试插入,外键就会乱码:

TARGET Human
Id      name      parentId
==========================
1       Armin     null
2       Cecil     1
3       Aron      null
4       Bert      1
5       Anna      2

TARGET Pet
Id      name      ownerId
==========================
1       Gatto     2 
2       Frankie   1
3       Doggo     2

Anna 的父亲是 Cecil,Doggo 的主人也是 Cecil 而不是 Bert。 Bert 的父母是 Armin 而不是 Aron。

我想要的样子是:

TARGET Human
Id      name      parentId
==========================
1       Armin     null
2       Cecil     1
3       Aron      null
4       Bert      3
5       Anna      4

TARGET Pet
Id      name      ownerId
==========================
1       Gatto     2 
2       Frankie   3
3       Doggo     4

想象一下,有 50 个类似的表,有 1000 行,所以我们必须自动解决方案。

问题

有我可以使用的特定工具吗?

是否有一些简单的 sql 逻辑可以精确地做到这一点?

我是否需要推出自己的软件来执行此操作(例如,连接到两个数据库的服务,读取 EF 中的所有内容以及所有关系,并将其保存到另一个数据库)?我担心问题太多,而且很费时间。

解决方法

有具体的工具吗?据我所知没有。
有一些简单的SQL吗?并不完全简单,但也不是那么复杂。
需要自己滚吗?也许,取决于您是否认为您使用 SQL (balow)。

我猜没有直接的路径,问题正如你所指出的,重新分配 FK 值。下面为所有表添加一列,可用于跨表。为此,我将使用 uuid。然后,您可以从一个表集复制到另一个表,除了 FK。复制后就可以加入uuid来完成FK了。

-- establish a reference field unique across databases. 
 alter table target_human add sync_id uuid default gen_random_uuid ();
 alter table target_pet   add sync_id uuid default gen_random_uuid ();
 alter table source_human add sync_id uuid default gen_random_uuid ();
 alter table source_pet   add sync_id uuid default gen_random_uuid ();  
 
--- copy table 2 to table 1 except parent_id 
 insert into target_human(name,sync_id)
   select name,sync_id 
     from source_human;
 
-- update parent id in table to prior parent in table 2 reasigning parent  
with conv (sync_parent,sync_child,new_parent) as 
     ( select h2p.sync_id sync_parent,h2c.sync_id sync_child,h1.id new_parent
         from source_human h2c
         join source_human h2p on h2c.parentid = h2p.id
         join target_human h1  on h1.sync_id = h2p.sync_id 
     ) 
update target_human  h1
   set parentid = c.new_parent
  from conv c 
 where h1.sync_id = c.sync_child;
----------------------------------------------------------------------------------------------- 
alter table target_pet alter column ownerId drop not null; 

insert into target_pet(name,sync_id) 
  select name,sync_id 
    from source_pet ;
    
with conv ( sync_pet,new_owner) as 
     ( select p2.sync_id,h1.id 
         from source_pet p2 
         join source_human h2  on p2.ownerid = h2.id
         join target_human h1  on h2.sync_id = h1.sync_id
     )  
update target_pet  p1
   set ownerid = c.new_owner 
  from conv c 
 where p1.sync_id = c.sync_pet; 
 
alter table target_pet alter column ownerId set not null; 
 

demo。您现在反转源表和目标表定义以完成同步的另一端。如果需要,您可以然后删除 uuid 列。但您可能想保留它们。如果您使它们不同步,您将再次这样做。您甚至可以更进一步,将 UUID 设为您的 PK/FK,然后只需复制数据,密钥将保持正确,但这可能涉及将应用程序更新为修改后的数据库结构。这不涉及跨数据库的通信,但我假设您已经处理了该问题。您将需要为每个集合重复,也许您可​​以编写一个脚本来生成它们。此外,我猜想与自己动手相比,陷阱更少,耗时更少。这基本上是您拥有的每个表集 5 个查询,但为了清理当前的混乱,500 个查询并没有那么多;

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。