2010-01-26 24 views
8

Tôi có lược đồ db cơ bản bao gồm 2 bảng; Một là một ID đơn giản -> Danh sách các thuật ngữ văn bản, và cái kia có 2 cột, cha và con. Các id trong bảng đầu tiên được tạo ra khi chèn bởi một chuỗi db trong khi bảng thứ hai chứa một ánh xạ giữa các khóa để lưu trữ 'cấu trúc' của cấu trúc phân cấp.Xuất/Nhập biểu đồ phân cấp từ cơ sở dữ liệu

Vấn đề của tôi là đôi khi tôi có thể di chuyển một cây từ db này sang db khác. Nếu tôi có 2 DB, mỗi nhóm có 10 thuật ngữ (thuật ngữ Cơ sở dữ liệu A! = Thuật ngữ Cơ sở dữ liệu B, và không có chồng chéo) và tôi chỉ sao chép dữ liệu từ A sang B thì tôi sẽ gặp vấn đề rõ ràng rằng các điều khoản sẽ là renumbered nhưng các mối quan hệ wont. Rõ ràng trong ví dụ này, việc thêm 10 vào tất cả các khóa quan hệ sẽ hoạt động, nhưng có ai biết thuật toán chung để làm điều này không?

Các DB là oracle 11g, và một giải pháp cụ thể oracle là tốt ...

+0

Không thực sự là câu trả lời nhưng bạn có cân nhắc sử dụng tập lệnh perl hoặc python để xử lý việc di chuyển không? – Pace

+0

Tôi sợ SQL đến cấu trúc cây thực sự giống như chốt vuông đến một lỗ tròn. Doable chỉ sử dụng quá nhiều lực lượng vũ phu. –

+0

Không phải là câu trả lời cho câu hỏi của bạn, nhưng có lý do cụ thể nào để có mối quan hệ cha/con của bạn trong một bảng riêng biệt không? Nếu mỗi cụm từ chỉ có thể có một phụ huynh, thì bảng điều khoản có thể có cột Gốc. Khi bạn cần tìm con, bạn có thể sử dụng câu lệnh 'select-from-connect by-start with'. Điều này cũng sẽ làm cho các nút gốc rõ ràng hơn, vì cột cha của chúng sẽ là rỗng. – Aaron

Trả lời

3

Tổng quan

tôi sẽ cung cấp cho bốn giải pháp, bắt đầu với đơn giản nhất. Với mỗi giải pháp, tôi sẽ giải thích các tình huống trong đó nó sẽ được áp dụng.

Mỗi giải pháp giả định rằng cơ sở dữ liệu A và B có các bảng sau:

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

create table Relationships 
(
    ParentID int, 
    ChildID int 
) 

Giải pháp 1

Đây là giải pháp đơn giản nhất. Nó nên được sử dụng nếu:

  • khoản với nội dung giống hệt nhau có thể được sáp nhập lại với nhau

Sau đây sẽ hợp nhất tất cả các điều khoản và các mối quan hệ từ A vào 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 

Về cơ bản bạn copy đầu tiên các điều khoản, sau đó sao chép các mối quan hệ ánh xạ id cũ sang id mới dựa trên văn bản.

Lưu ý: Trong câu hỏi của bạn, bạn nêu rõ các điều khoản được phân tách giữa hai cơ sở dữ liệu đầu vào. Trong trường hợp đó, mệnh đề where trong insert into đầu tiên có thể bị bỏ qua.

Giải pháp 2

Đây là giải pháp tiếp theo đơn giản nhất. Nó nên được sử dụng nếu:

  • khoản có cùng nội dung phải được giữ riêng biệt, và
  • Bạn có thể thêm một cột vào bảng điểm đến

Đầu tiên thêm một cột int bảng khoản của bạn gọi là "OldID", sau đó sử dụng sau đây để hợp nhất tất cả các điều khoản và các mối quan hệ từ A đến 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 

giải pháp 3

Giải pháp này sử dụng lặp lại.Nó nên được sử dụng nếu:

  • khoản có cùng nội dung phải được giữ riêng biệt, và
  • Bạn không thể sửa đổi các bảng đích, và
  • Hoặc (a) cột ID của bạn là một cột sắc (trong Oracle, điều này có nghĩa nó có một kích hoạt sử dụng một chuỗi), hoặc (b) bạn muốn có một phương pháp chung mà sẽ làm việc với bất kỳ công nghệ cơ sở dữ liệu

sau đây sẽ hợp nhất tất cả các điều khoản và relat ionships từ A vào 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 

Giải pháp 4

Giải pháp này là Oracle-cụ thể, đòi hỏi bạn phải biết trình tự sử dụng để tạo ra giá trị ID, và kém hiệu quả hơn so với một số các giải pháp khác . Nó nên được sử dụng nếu:

  • khoản có cùng nội dung phải được giữ riêng biệt, và
  • Bạn không thể sửa đổi các bảng đích, và
  • Bạn có thể truy cập đến trình tự mà tạo ra cột ID của bạn, và
  • Bạn đang sử dụng một ok techinique rằng sẽ không cổng để một công nghệ cơ sở dữ liệu Oracle phi

sau đây sẽ hợp nhất tất cả các điều khoản và các mối quan hệ từ A vào 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 không có bất cứ điều gì tương đương với cột nhận dạng (ít nhất là nó không khi tôi sử dụng nó lần cuối). Điều này có thể có liên quan đến SQL Server nhưng bạn sẽ không cần phải làm điều đó trên Oracle. – ConcernedOfTunbridgeWells

+0

Trên thực tế Oracle không có tương đương với cột nhận dạng: Trình kích hoạt cùng với chuỗi. Một cơ sở dữ liệu được thiết kế tốt sẽ sử dụng nó. Visage có thể cần sử dụng 'giải pháp 3' của tôi trong Oracle nếu anh ta không có quyền truy cập vào chuỗi tạo ra giá trị ID. Cũng trong những trường hợp thông thường, anh ta có thể nhận được bằng 'Solution 1' hoặc' Solution 2', cả hai đều đơn giản và hiệu quả hơn vì chúng không tạo ra một bảng tạm thời. Đối với các tình huống mà chuỗi * có thể truy cập được, tôi đã thêm một 'Giải pháp 4' cho thấy cách sử dụng nó để tránh lặp lại. –

5

câu trả lời nhanh

nhập vào một bảng dàn, nhưng cư ánh xạ ID giá trị so với cùng chuỗi được sử dụng để tạo các giá trị ID từ bảng đích. Điều này được đảm bảo để tránh xung đột giữa các giá trị ID như động cơ DBMS hỗ trợ truy cập đồng thời vào các chuỗi.

Với các giá trị ID trên nút được ánh xạ (xem bên dưới), ánh xạ lại các giá trị ID cho các cạnh là tầm thường.

Còn câu trả lời

Bạn sẽ cần một cơ chế mà các bản đồ các giá trị giữa các phím cũ từ nguồn và phím mới trong đích. Cách để làm điều này là tạo ra các bảng dàn xếp trung gian giữ các ánh xạ giữa các kays cũ và mới.

Trong Oracle, các phím autoincrementing thường được thực hiện với các trình tự theo cách bạn mô tả. Bạn cần phải xây dựng các bảng dàn dựng với một trình giữ chỗ cho khóa 'cũ' để bạn có thể thực hiện ánh xạ lại. Sử dụng cùng một trình tự như được ứng dụng sử dụng để điền các giá trị ID trên các bảng cơ sở dữ liệu đích thực tế. DBMS cho phép truy cập đồng thời vào các chuỗi và sử dụng cùng một trình tự đảm bảo rằng bạn sẽ không nhận được xung đột trong các giá trị ID được ánh xạ.

Nếu bạn có một sơ đồ như:

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 
) 
/

này sẽ cho phép bạn nhập vào bảng STAGE_NODE, giữ gìn các giá trị quan trọng nhập khẩu. Quá trình chèn đặt ID ban đầu từ bảng đã nhập vào STAGED_ID và điền ID từ trình tự.

Đảm bảo bạn sử dụng cùng một chuỗi được sử dụng để điền cột ID trong bảng đích. Điều này đảm bảo rằng bạn sẽ không nhận các xung đột chính khi bạn truy cập chèn vào bảng đích cuối cùng. Điều quan trọng là phải sử dụng lại cùng một trình tự.

Là một tác dụng phụ hữu ích, điều này cũng sẽ cho phép quá trình nhập chạy trong khi các hoạt động khác đang diễn ra trên bảng; đồng thời đọc trên một chuỗi duy nhất là tốt. Nếu cần thiết, bạn có thể chạy loại quy trình nhập này mà không cần phải gỡ bỏ applciation.

Một khi bạn đã lập bản đồ này trong bảng dàn, giá trị ID trong bảng EDGE là tầm thường để tính toán với một truy vấn như:

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 

Các giá trị EDGE ánh xạ có thể được dân cư trở lại vào các bảng dàn bằng cách sử dụng truy vấn UPDATE có tham gia tương tự hoặc được chèn trực tiếp vào bảng đích từ truy vấn tương tự như trên.

0

Tôi từng làm điều này rất nhiều, nhưng bộ nhớ của tôi hơi mơ hồ. Tôi sẽ cho bạn ý tưởng chung, hy vọng nó có thể giúp bạn chỉ đúng hướng.

Về cơ bản, bạn chỉ có thể thực hiện việc này nếu bạn có cột 'khóa duy nhất' đáng tin cậy trong bảng 'cha'. Nếu không, bạn sẽ cần phải tạo một.

Giả sử chúng ta có các bảng

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

HIERARCHY[idparent, idchild] 

Những gì bạn muốn làm là MẶT bản sao đầu tiên từ SOURCEDB để TARGETDB, cho phép TARGETDB tạo các giá trị riêng của mình cho các cột id.

Sau đó, bạn cần phải sao chép HIERARCHY từ SOURCEDB để TARGETDB, nhưng bạn cần phải làm một tham gia như vậy để có được id mới:

Và bạn cần phải làm điều tương tự cho cột idchild.

này sẽ cung cấp một cái gì đó như thế này (chưa được kiểm tra cú pháp, và rỉ sét, và có lẽ 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 

Tôi giả sử rằng hai cơ sở dữ liệu này được 'kết nối' đủ để bạn có thể làm các truy vấn chéo cơ sở dữ liệu . Nếu bạn phải serialize để tập tin, nó sẽ phức tạp hơn một chút.

0

Bạn có thể đạt được những gì bạn cần bằng bảng tạm thời trong cơ sở dữ liệu đích. Vì ID được tạo tự động, mã sau sẽ không tạo ra bất kỳ xung đột nào.

Tôi sẽ giả sử cơ sở dữ liệu nguồn được gọi là SourceDb và cơ sở dữ liệu đích được gọi là TargetDb. Tôi cũng sẽ assum cấu trúc này bảng:
Điều khoản: ID, chữ
Mối quan hệ: ParentId, ChildId

Tạo một bảng tạm thời trong TargetDb với điều này cấu trúc:
TempTerms: OldId, chữ, OldParentId, newid, NewParentId

Các mã sau đây sẽ sao chép cây con của mình vào cơ sở dữ liệu mục tiêu.

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

Còn về việc truyền dữ liệu dưới dạng XML thì sao? Nó được thiết kế tự nhiên để làm việc với các cấu trúc cây và nhiều DBMS bao gồm hỗ trợ tốt cho phân tích cú pháp và chuyển đổi XML.

Bạn sẽ phải đảm bảo nút X trong DB1 ánh xạ tới nút Y trong DB 2, nhưng bạn nên sử dụng một số thông tin về nút (tên, v.v) ngoài khóa chính.

Bạn cũng có thể bù đắp các khóa cho mỗi DB bằng một số tiền thông thường (giả sử 2^32) và sử dụng khóa BIG INTEGER. Giới hạn mục nhập đến 2^32, nhưng vẫn hữu ích.

(Tôi có thể hiểu nhầm câu hỏi ở đây, nhưng tôi hy vọng là không.)

Các vấn đề liên quan