2010-01-26 60 views
8

我有一个包含2个表的基本数据库结构;一个是简单的ID - >文本列表的术语,另一个是2列,父母和孩子。第一个表中的ID是由db序列插入时生成的,而第二个表包含存储层次结构'结构'的键之间的映射。从数据库导出/导入分层结构图

我的问题是,我可能想有时移动一棵树从一个数据库到另一个。如果我有2个数据库,每个数据库有10个条款(数据库A的条款!=数据库B的条款,并且没有重叠),并且我只是将数据从A复制到B,那么我会遇到一个显而易见的问题,即条款将会重新编号,但关系不会。很明显,在这个例子中,只需在所有关系键中添加10就可以工作,但是有谁知道这样做的一般算法?

的DB是的Oracle 11g和Oracle具体的解决方案是好的...

+0

不是一个真正的答案,但你有没有考虑用Perl或Python脚本来处理的举动? – Pace 2010-01-29 13:37:06

+0

我担心树结构的SQL真的很像一个圆形的圆孔。只能使用过量的暴力。 – 2010-01-29 13:39:43

+0

不是你的问题的答案,但有一个特定的理由让你的父母/孩子的关系在一个单独的表?如果每个术语只能有一个父项,则术语表可以有一个父项列。当你需要找到孩子时,你可以使用'select-from-connect by-start with'语句。这也会使根节点更加明显,因为它们的父列将为空。 – Aaron 2010-01-29 15:04:55

回答

3

概述

我会给四种解决方案,从最简单的。每个解决方案我都会解释它适用的情况。

这些解决方案中的每一个假设数据库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 

基本上你第一个副本然后根据文本复制将旧ID映射到新ID的关系。

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

溶液2

这是下一个最简单的解决方案。它应该被用来当:

  • 条款具有相同文本必须保持不同,并
  • 可以列到目标表

首先添加一个int的列到你的条款表所谓“OLDID”,然后用下面从合并的所有条款和关系,以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

此解决方案使用迭代。它应该被用来当:

  • 条款具有相同文本必须保持不同,并
  • 不能修改目标表,并
  • 无论(一)您的ID列是标识列(在Oracle中,这意味着它有一个使用序列触发),或(b)你想将与任何数据库技术工作的一般方法

以下将合并所有的条款和RELAT ionships从A到B:

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 

解决方案4

该溶液是Oracle特定的,需要你知道用于生成ID值的序列,并且是比一些其他的解决方案的效率较低。它应该被用来当:

  • 条款具有相同文本必须保持不同,并
  • 不能修改目标表,并
  • 您可以访问生成的ID列的顺序,并
  • 一切都OK使用techinique不会移植到非Oracle数据库技术

以下将合并所有方面和关系从A到B:

-- 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 
+0

Oracle没有任何与身份列相同的东西(至少在我上次使用它时没有)。这可能与SQL Server相关,但您不需要在Oracle上执行此操作。 – ConcernedOfTunbridgeWells 2010-02-01 10:44:04

+0

实际上,Oracle确实具有与标识列等效的内容:一个触发器和一个序列。一个设计良好的数据库将使用它。如果他不能访问生成ID值的序列,Visage可能需要在Oracle中使用我的“解决方案3”。同样在一般情况下,他可以通过“解决方案1”或“解决方案2”获得解决方案,因为它们不会创建临时表,所以既简单又高效。对于可以访问序列*的情况,我添加了一个“解决方案4”,显示如何使用它来避免迭代。 – 2010-02-01 21:29:12

5

快速回答

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

通过映射节点上的ID值(请参阅下面的内容),重新映射边缘的ID值是微不足道的。

再回应

您将需要这些值映射从源头上旧键和新键在目的地之间的机制。做到这一点的方法是创建中间登台表,以保存旧的和新的线索之间的映射。

在Oracle中,自动增量键通常按照您描述的方式使用顺序完成。您需要使用“旧”键的占位符构建临时表,以便重新映射。使用与应用程序使用的相同序列来填充实际目标数据库表上的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_NODE表,保留进口键值。插入过程将导入表中的原始ID放入STAGED_ID中,并从序列中填充ID。

确保您使用的是用于 目标表填充ID列 相同的序列。这可以确保当您将 插入到最终的目的地表格时,您不会 获得重要冲突。 重复使用相同的序列很重要。

作为一个有用的副作用,这也将允许导入运行而其他操作正在桌子上进行;对单个序列的并发读取很好。如有必要,您可以运行这种类型的导入过程,而不必关闭应用程序。

一旦你在临时表该映射,在EDGE表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 

映射的边缘值可以填充回到分级表使用具有类似连接的UPDATE查询或从类似于上面的查询中直接插入到目标表中。

0

我曾经做过这种事情很多,但我的记忆有点朦胧。我会给你一个总的想法,希望能让你指出正确的方向。

基本上,只有在'父'表中有可靠的第二'唯一键'列时才能做到这一点。如果没有,你需要创建一个。

说我们有这些表

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:

而你需要做同样的事情为idchild列。

这将给像这样(未经测试,和生锈,而且很可能MSSQL语法):

//step 1 
INSERT TARGETDB.ITEMS(A, key) 
SELECT A, key FROM SOURCEDB.ITEMS 

//step 2 
INSERT TARGETDB.HIERARCHY(idparent, idchild) 
SELECT T1.id, T2.id 
FROM SOURCEDB.HIERARCHY AS H1 
    INNER JOIN SOURCEDB.ITEMS AS I1 ON H1.idparent = I1.id 
    INNER JOIN TARGETDB.ITEMS AS T1 ON I1.key = T1.key 
    INNER JOIN SOURCEDB.ITEMS AS I2 ON H1.idchild = I2.id 
    INNER JOIN TARGETDB.ITEMS AS T2 ON I2.key = T2.key 

我假定这两个数据库的“连接”,以至于你可以做跨数据库查询。如果你必须连载文件,它会更复杂一点。

0

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

我打算假定源数据库被称为SourceDb,而目标数据库被称为TargetDb。 我也要去assum此表结构:
条款ID文本
关系的ParentIdchildID的

在TargetDb创建临时表与此结构:
TempTermsOldId文本OldParentIdNEWIDNewParentId

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

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; 
0

将XML作为数据传递的过程如何?它自然设计用于处理树结构,许多DBMS包括对XML解析和转换的良好支持。

您必须确保DB1中的节点X映射到数据库2中的节点Y,但为此您应该使用有关主键之外的节点(名称等)的一些事实。

您也可以将每个DB的密钥按照一定的数量(比如2^32)进行偏移并使用BIG INTEGER键。限制条目为2^32,但仍然有帮助。

(我可以在这里误解的问题,但我希望不会。)