Database 从数据库导出/导入分层图

Database 从数据库导出/导入分层图,database,export,Database,Export,我有一个基本的数据库模式,包括2个表;一个是简单的ID->术语文本列表,另一个有两列,父列和子列。第一个表中的ID在insert时由db序列生成,而第二个表包含键之间的映射以存储层次结构的“结构” 我的问题是,有时我可能想将树从一个db移动到另一个db。如果我有2个数据库,每个数据库中有10个术语(数据库A的术语!=数据库B的术语,并且没有重叠),并且我只是将数据从A复制到B,那么我会遇到一个明显的问题,即术语将被重新编号,但关系不会。显然,在本例中,只需向所有关系键添加10个键就可以了,但是有

我有一个基本的数据库模式,包括2个表;一个是简单的ID->术语文本列表,另一个有两列,父列和子列。第一个表中的ID在insert时由db序列生成,而第二个表包含键之间的映射以存储层次结构的“结构”

我的问题是,有时我可能想将树从一个db移动到另一个db。如果我有2个数据库,每个数据库中有10个术语(数据库A的术语!=数据库B的术语,并且没有重叠),并且我只是将数据从A复制到B,那么我会遇到一个明显的问题,即术语将被重新编号,但关系不会。显然,在本例中,只需向所有关系键添加10个键就可以了,但是有人知道这样做的通用算法吗


DB是oracle 11g,oracle特定的解决方案很好…

快速回答

导入到暂存表中,但从用于从目标表生成ID值的相同序列填充映射的ID值。这可以保证避免ID值之间的冲突,因为DBMS引擎支持对序列的并发访问

当节点上的ID值已映射(请参见下文)时,重新映射边的ID值非常简单

更长的答案

您需要一种机制来映射源中的旧键和目标中的新键之间的值。实现这一点的方法是创建包含新旧KAY之间映射的中间暂存表

在Oracle中,自动递增键通常以您描述的方式使用序列来完成。您需要使用“old”键的占位符来构造临时表,以便可以重新映射。使用与应用程序用于填充实际目标数据库表上的ID值相同的顺序。DBMS允许对序列进行并发访问,并且使用相同的序列可以保证在映射的ID值中不会发生冲突

如果您的模式类似于:

create table STAGE_NODE (
       ID int
      ,STAGED_ID int
)
/

create table STAGE_EDGE (
       FROM_ID   int
      ,TO_ID     int
      ,OLD_FROM_ID int
      ,OLD_TO_ID int
)
/
这将允许您导入到
STAGE\u节点
表中,保留导入的键值。插入过程将导入表中的原始ID放入STAGED_ID中,并从序列中填充ID

确保您使用的序列与 用于填充中的ID列 目标表。这确保了您不会 转到时获取关键点碰撞 插入到最终目标表。 重复使用相同的序列很重要。

作为一个有用的副作用,这还将允许在表上进行其他操作时运行导入;对单个序列进行并发读取是可以的。如有必要,您可以在不关闭应用程序的情况下运行这种类型的导入过程

一旦在暂存表中有了此映射,边缘表中的ID值就可以通过以下查询进行计算:

select node1.ID         as FROM_ID
      ,node2.ID         as TO_ID
  from STAGE_EDGE se
  join STAGE_NODE node1
    on node1.STAGED_ID = se.OLD_FROM_ID
  join STAGE_NODE node2
    on node2.STAGED_ID = se.OLD_TO_ID 
映射的边缘值可以使用具有类似联接的更新查询填充回暂存表,也可以从类似于上面的查询直接插入到目标表中。

我以前经常做这种事情,但我的内存有点模糊。我会给你一个大概的想法,希望它能为你指明正确的方向

基本上,只有在“parent”表中有可靠的第二个“unique key”列时,才能执行此操作。如果没有,则需要创建一个

假设我们有这些桌子

ITEMS[id, A, key] //id: 'real' id, A: just some column, key: the alternate key

HIERARCHY[idparent, idchild]
您要做的是首先将项目从SOURCEDB复制到TARGETDB,让TARGETDB为id列创建自己的值

然后,您需要将层次结构从SOURCEDB复制到TARGETDB,但需要执行类似的连接以获取新id:

SOURCEDB.HIERARCHY.idparent 
      -> SOURCEDB.ITEMS.id 
      -> SOURCEDB.ITEMS.key 
      -> TARGETDB.ITEMS.key 
      -> TARGETDB.ITEMS.id
您需要对idchild列执行相同的操作

这将给出如下内容(未经测试、生锈,可能还有mssql语法):


我假设这两个数据库足够“连接”,可以进行跨数据库查询。如果您必须序列化到文件,它会得到更多。。。复杂。

您可以使用目标数据库中的临时表实现所需的功能。 由于ID是自动生成的,下面的代码不会生成任何冲突

我假设源数据库名为SourceDb,目标数据库名为TargetDb。 我还要假设这个表结构:
术语:ID,文本
关系:父ID、子ID

使用以下结构在TargetDb中创建临时表:
试探项:OldId、Text、OldParentId、NewId、NewParentId

下面的代码将您的子树复制到目标数据库

declare
    RootOfSubtreeId SourceDb.Terms.Id%type;
    TermCursor sys_refcursor;
begin
    --//Copy the data from SourceDb into the TargetDb temp table.
    --//This query gets the entire subtree of data with the root of the subtree having ID=RootOfSubTreeId.
    insert into TargetDb.TempTerms
    (
        OldId, Text, OldParentId
    )
    with RelatedTerms as
    (
        select
            T.ID, T.Text, R.ParentId
        from
            SourceDb.Terms T
            join SourceDb.Relationships R
            on R.ChildId = T.ID
    )
    select
        ID,
        Text,
        ParentId
    from
        RelatedTerms
    connect by
        prior ID = ParentId
    start with
        ID = RootOfSubtreeId;

    --//Open a cursor to loop over all of the temporary data.
    open TermCursor for
    select
        *
    from
        TargetDb.TempTerms;

    for term in TermCursor
    loop
        --//Insert the item into TargetDb's Terms table and get the new id back.
        insert into TargetDb.Terms
        ( ID, Text )
        values
        ( term.Text )
        returning ID into NewTermId;

        --//Update the temp table's NewId column for the newly inserted row.
        update TargetDb.TempTerms
        set    NewId = NewTermId
        where  OldId = term.OldId;

        --//Update the temp table's NewParentId column for all children of the newly inserted row.
        update TargetDb.TempTerms
        set    NewParentId = NewTermId
        where  OldParentId = term.OldId;
    end loop;

    --//Add all relationship data to TargetDb using the new IDs found above.
    insert into TargetDb.Relationships
    ( ParentId, ChildId )
    select
        NewParentId, NewId
    from
        TargetDb.TempTerms
    where
        NewParentId is not null;
end;

概述

我将给出四个解决方案,从最简单的开始。对于每个解决方案,我将解释其适用的情况

这些解决方案都假设数据库A和B具有以下表格:

create table Terms
(
  ID int identity(1,1),
  Text nvarchar(MAX)
)

create table Relationships
(
  ParentID int,
  ChildID int
)
解决方案1

这是最简单的解决办法。应在以下情况下使用:

  • 文本相同的术语可以合并在一起
以下内容将把A中的所有术语和关系合并到B中:

insert into A.Terms (Text)
  select Text
  from A.Terms
  where Text not in (select Text from B.Terms)

insert into B.Relationships (ParentID, ChildID)
  select
    (select ID
     from B.Terms BTerms inner join A.Terms ATerms on BTerms.Text = ATerms.Text
     where ATerms.ID = Relationships.ParentID),
    (select ID
     from B.Terms BTerms inner join A.Terms ATerms on BTerms.Text = ATerms.Text
     where ATerms.ID = Relationships.ChildID)
  from A.Relationships
declare TermsCursor sys_refcursor; 
begin 

-- Create temporary mapping table
create table #Temporary (OldID int, NewID int)

-- Add terms one at a time, remembering the id mapping
open TermsCursor for select * from A.Terms;
for term in TermsCursor 
loop
  insert into B.Terms (Text) values ( term.Text ) returning ID into NewID;
  insert into Temporary ( OldID, NewID ) values ( term.ID, NewID );
end loop; 

-- Transfer the relationships
insert into B.Relationships (ParentID, ChildID)
  select
    (select ID
     from B.Terms BTerms inner join Temporary on BTerms.ID = Temporary.NewID
     where Temporary.OldID = Relationships.ParentID),
    (select ID
     from B.Terms BTerms inner join Temporary on BTerms.ID = Temporary.NewID
     where Temporary.OldID = Relationships.ChildID),
  from A.Relationships

-- Drop the temporary table
drop table #Temporary

end
-- Create temporary mapping table
create table #Temporary (OldID int, NewID int)

-- Add terms to temporary mapping table
insert into #Tempoarary ( OldID, NewID )
select ID, sequence.nexval
from A.Terms

-- Transfer the terms
insert into B.Terms ( ID, Text )
select NewID, Text
from A.Terms inner join Temporary on ID = OldID

-- Transfer the relationships
insert into B.Relationships (ParentID, ChildID)
  select
    (select ID
     from B.Terms BTerms inner join Temporary on BTerms.ID = Temporary.NewID
     where Temporary.OldID = Relationships.ParentID),
    (select ID
     from B.Terms BTerms inner join Temporary on BTerms.ID = Temporary.NewID
     where Temporary.OldID = Relationships.ChildID),
  from A.Relationships

-- Drop the temporary table
drop table #Temporary
基本上,首先复制术语,然后根据文本复制将旧id映射到新id的关系

注意:在您的问题中,您声明两个输入数据库之间的术语是不相交的。在这种情况下,可以省略第一个插入到中的
where
子句

解决方案2

这是下一个最简单的解决方案。应在以下情况下使用:

  • 具有相同文本的术语必须保持不同,并且
  • 可以向目标表中添加列
首先在术语表中添加一个名为“OldID”的int列,然后使用以下命令将所有术语和关系从A合并到B:

insert into A.Terms (Text, OldID)
  select Text, ID
  from A.Terms
  where Text not in (select Text from B.Terms)

insert into B.Relationships (ParentID, ChildID)
  select
    (select ID from B.Terms where OldID = ParentID),
    (select ID from B.Terms where OldID = ChildID)
  from A.Relationships
解决方案3

此解决方案使用它
-- Create temporary mapping table
create table #Temporary (OldID int, NewID int)

-- Add terms to temporary mapping table
insert into #Tempoarary ( OldID, NewID )
select ID, sequence.nexval
from A.Terms

-- Transfer the terms
insert into B.Terms ( ID, Text )
select NewID, Text
from A.Terms inner join Temporary on ID = OldID

-- Transfer the relationships
insert into B.Relationships (ParentID, ChildID)
  select
    (select ID
     from B.Terms BTerms inner join Temporary on BTerms.ID = Temporary.NewID
     where Temporary.OldID = Relationships.ParentID),
    (select ID
     from B.Terms BTerms inner join Temporary on BTerms.ID = Temporary.NewID
     where Temporary.OldID = Relationships.ChildID),
  from A.Relationships

-- Drop the temporary table
drop table #Temporary