我有一个基本的数据库模式,包括两个表;一个是简单的ID -> Text list of terms,另一个有2列,parent和child。第一个表中的in是在插入时由db序列生成的,而第二个表包含键之间的映射,以存储分层结构的“结构”。
我的问题是,有时我可能想要将树从一个数据库移动到另一个数据库。如果我有两个DB,每个DB都有10个术语(数据库A的术语!=数据库B的术语,没有重叠),而我只是将数据从A复制到B,那么我会得到一个明显的问题,即术语将被重新编号,但关系不会。显然,在这个例子中,只需将10加到所有的关系键上就可以了,但是有谁知道一个通用的算法来做到这一点吗?
数据库是oracle 11g,一个特定于oracle的解决方案就可以了…
发布于 2010-01-30 02:26:51
概述
我将给出四个解决方案,从最简单的开始。对于每个解决方案,我将解释它适用的情况。
这些解决方案中的每一个都假定数据库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
这是下一个最简单的解决方案。在以下情况下应使用它:
具有相同文本的
首先在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
发布于 2010-01-29 21:40:41
快速回答
导入到临时表中,但从用于从目标表生成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列相同的顺序。这可以确保在插入到最终目标表时不会发生键冲突。重用相同的sequence.As很重要,这是一个有用的副作用,它还允许在对表执行其他操作的同时运行导入;对单个序列进行并发读取就可以了。如有必要,您可以在不关闭应用程序的情况下运行此类型的导入过程。
一旦在临时表中有了这个映射,就很容易使用如下查询来计算边缘表中的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查询重新填充到临时表中,或者直接从与上面类似的查询中插入到目标表中。
发布于 2010-01-29 22:34:10
我以前经常做这样的事情,但我的记忆力有点模糊。我会给你一个大概的想法,希望它能给你指明正确的方向。
基本上,只有在“parent”表中有一个可靠的第二个“唯一键”列时,才能做到这一点。如果没有,您将需要创建一个。
假设我们有这些表
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语法):
//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
我假设这两个数据库有足够的“连接”,你可以进行跨数据库查询。如果你必须序列化到文件,它会变得更多一点...很复杂。
https://stackoverflow.com/questions/2140512
复制相似问题