我有一个包含2个表的基本数据库模式;一个是简单的ID - >术语的文本列表,另一个有2列,父和子。第一个表中的ID是在db序列插入时生成的,而第二个表包含用于存储层次结构的“结构”的键之间的映射。
我的问题是我有时可能希望将树从一个数据库移动到另一个数据库。如果我有2个DB,每个有10个术语(数据库A的术语!=数据库B的术语,并且没有重叠),我只是将数据从A复制到B然后我会遇到一个明显的问题,即术语将是重新编号,但关系不会。显然,在这个例子中,只需在所有关系键上添加10即可,但有没有人知道这样做的一般算法?
DB是oracle 11g,oracle特定的解决方案很好......
答案 0 :(得分:5)
快速回答
导入到临时表中,但是从用于从目标表生成ID值的相同序列 中填充映射的ID值 。这可以保证避免ID值之间的冲突,因为DBMS引擎支持对序列的并发访问。
使用映射的节点上的ID值(见下文)重新映射边缘的ID值是微不足道的。
更长的答案
您需要一种机制来映射来自源中的旧键和目标中的新键之间的值。这样做的方法是创建中间登台表,用于保存新旧kays之间的映射。
在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查询将映射的EDGE值填充回登台表,或者从类似于上述查询的查询中直接插入到目标表中。
答案 1 :(得分: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的关系。
注意:在您的问题中,您说明两个输入数据库之间的术语是不相交的。在这种情况下,可以省略第一个where
中的insert into
子句。
解决方案2
这是下一个最简单的解决方案。它应该用于:
首先在您的Terms表中添加一个名为“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
此解决方案使用迭代。它应该用于:
以下内容将合并从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值的序列,并且效率低于某些其他解决方案。它应该用于:
以下内容将合并从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
答案 2 :(得分:0)
我曾经做过很多这样的事,但我的记忆有点朦胧。我会给你一般的想法,希望它可以让你指出正确的方向。
基本上,只有在“父”表中有一个可靠的第二个“唯一键”列时,才能执行此操作。如果没有,你需要创建一个。
假设我们有这些表
ITEMS[id, A, key] //id: 'real' id, A: just some column, key: the alternate key
HIERARCHY[idparent, idchild]
您要做的是首先将SOEMS从SOURCEDB复制到TARGETDB,让TARGETDB为id列创建自己的值。
然后你需要将HIERARCHY从SOURCEDB复制到TARGETDB,但你需要像这样进行连接以获得新的id:
SOURCEDB.HIERARCHY.idparent
-> SOURCEDB.ITEMS.id
-> SOURCEDB.ITEMS.key
-> TARGETDB.ITEMS.key
-> TARGETDB.ITEMS.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
我假设这两个数据库已“足够连接”,您可以进行跨数据库查询。如果你必须序列化到文件,它会变得更复杂。
答案 3 :(得分:0)
您可以使用目标数据库中的临时表来实现所需。 由于ID是自动生成的,因此以下代码不会产生任何冲突。
我将假设源数据库名为SourceDb,目标数据库名为TargetDb。
我也打算对这个表结构进行讨论:
条款: ID ,文字
关系: ParentId , ChildId
使用以下结构在TargetDb中创建临时表:
TempTerms : OldId ,文字, OldParentId , NewId ,< EM> 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;
答案 4 :(得分:0)
如何将数据作为XML传递?它自然地设计用于树结构,许多DBMS包括对XML解析和转换的良好支持。
您必须确保DB1中的节点X映射到DB 2中的节点Y,但为此您应该使用关于主键之外的节点(名称等)的一些事实。
您还可以按常规量(例如2 ^ 32)偏移每个数据库的键,并使用BIG INTEGER键。将条目限制为2 ^ 32,但仍然有用。
(我可能在这里误解了这个问题,但我希望不会。)