2010-01-26 28 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,但仍然有幫助。

(我可以在這裏誤解的問題,但我希望不會。)