2011-10-31 61 views
2

我有4個相關的表,每個表與下一個表有1:N的關係,例如,複製mssql中的多級關聯表

One (OneID pk) 
Two (TwoID pk, OneID fk) 
Three (ThreeID pk, TwoID fk) 
Four (FourID pk, ThreeID fk) 

我需要實現功能,當用戶想要複製'一個'記錄和表中的所有相關記錄二,三和四。

從前端開始,用戶可以在現有的基礎上創建新的記錄。做這個的最好方式是什麼?我有新插入的「OneID」和原始的「OneID」。

我想過這樣做的一種方式是爲每個表都有一個「複製」存儲過程,每個表中都有一個將其稱爲子表的副本。

我認爲做這件事的唯一方法是有一個臨時表,它有每張表的原始+新ID的記錄,但是這看起來很混亂,並且像它可能失去控制。

有什麼建議嗎?

+0

我會做一個存儲過程。事實上,在使用存儲過程之前,我實現了這樣的功能。不過,我不會使用遊標。 W/O的細節很難說,但我想你可以做一些'插入...'並且爲每個表格複製所有內容。 – Icarus

+0

我一直在努力的部分是,當我插入'Two'時,如何獲得新的/舊的TwoID值以準確更新'Three'? – Fermin

回答

0

我不得不在過去爲大量數據做到這一點。我發現爲每個表使用存儲過程,臨時表和GUID列的最佳方式。在我的情況下,我們有一個存儲過程爲所涉及的所有表執行所有複製,但是如果您願意,您可以爲每個表執行一次。我們創建了一組臨時表,這些表是我們將從中複製的所有表的精確副本,但是是以不同的模式並且沒有鍵。複製時,我們將所有記錄的副本插入臨時表中。然後,我們從臨時表中插入到dbo表中。這個想法是首先插入沒有FK的記錄(這些是頂級項目)。然後,在臨時區域中,任何具有對剛插入的頂級記錄的引用的記錄都將其FK字段更新到臨時表中,然後插入到dbo中。您會發現,GUID列的原因在於,這是將複製的記錄重新綁定到原始文件以更新foriegn密鑰的唯一方法。如果您有4條記錄全部通過foriegn關鍵關係綁定在一起,那麼您希望所有複製的記錄以相同的方式綁定在一起。唯一的方法是以某種方式跟蹤原件的ID和副本的ID,並相應地更新它們。如果您要進行批量插入(就像我們一樣),那麼GUID列是我們找到的唯一解決方案,但是如果所有插入都是單個記錄,那麼表中可能不需要GUID列。下面是它如何去使用GUID列一個快速的想法:

-- copy data from dbo to temp schema 
INSERT temp.One (field1, field2, guid, etc) 
SELECT field1, field2, guid, etc 
    FROM dbo.One 
WHERE OneID = @OneID 

INSERT temp.Two (field1, field2, guid, etc) 
SELECT field1, field2, guid, etc 
    FROM dbo.Two t 
INNER JOIN temp.One o ON o.OneID = t.OneID 

... 

-- update GUIDs in temp area 
UPDATE temp.One 
    SET guid = NEWID() 

UPDATE temp.Two 
    SET guid = NEWID() 

... 

-- insert from temp to dbo 
INSERT dbo.One (field1, field2, guid, etc) 
SELECT field1, field2, guid, etc 
    FROM temp.One 

-- need to update FK here before inserting to dbo, join from temp to dbo on GUID 
UPDATE temp.Two 
    SET OneID = c.OneID 
    FROM temp.Two t 
INNER JOIN temp.One o ON t.OneID = o.OneID 
INNER JOIN dbo.One c ON c.GUID = o.GUID 

INSERT dbo.Two (field1, field2, guid, etc) 
SELECT field1, field2, guid, etc 
    FROM temp.Two 

... 
0

你基本上只需要一個表來映射你的舊/新值,物理表,如果你想將這些資料保存的記錄,一個臨時表,如果你沒有。

-- Create Tables 
    CREATE TABLE #one (oneid UNIQUEIDENTIFIER) 
    CREATE TABLE #two (twoid UNIQUEIDENTIFIER, oneid UNIQUEIDENTIFIER) 
    CREATE TABLE #three (threeid UNIQUEIDENTIFIER, twoid UNIQUEIDENTIFIER) 
    CREATE TABLE #four (fourid UNIQUEIDENTIFIER, threeid UNIQUEIDENTIFIER) 

-- Insert test data 
    DECLARE @guid UNIQUEIDENTIFIER 
    SET @guid = newid() 
    insert #one values (@guid) 

    INSERT #two select NEWID(), oneid from #one 
    INSERT #two select NEWID(), oneid from #one 
    INSERT #two select NEWID(), oneid from #one 

    INSERT #three SELECT NEWID(), twoid FROM #two WHERE oneid = @GUID 
    INSERT #three SELECT NEWID(), twoid FROM #two WHERE oneid = @GUID 
    INSERT #three SELECT NEWID(), twoid FROM #two WHERE oneid = @GUID 

    INSERT #four SELECT NEWID(), threeid FROM #three WHERE twoid IN (SELECT twoid FROM #two WHERE oneid = @GUID) 
    INSERT #four SELECT NEWID(), threeid FROM #three WHERE twoid IN (SELECT twoid FROM #two WHERE oneid = @GUID) 
    INSERT #four SELECT NEWID(), threeid FROM #three WHERE twoid IN (SELECT twoid FROM #two WHERE oneid = @GUID) 


-- Create temp tables 
    CREATE TABLE #tempone (oneid UNIQUEIDENTIFIER, oldval UNIQUEIDENTIFIER) 
    CREATE TABLE #temptwo (twoid UNIQUEIDENTIFIER, oneid UNIQUEIDENTIFIER, oldval UNIQUEIDENTIFIER) 
    CREATE TABLE #tempthree (threeid UNIQUEIDENTIFIER, twoid UNIQUEIDENTIFIER, oldval UNIQUEIDENTIFIER) 
    CREATE TABLE #tempfour (fourid UNIQUEIDENTIFIER, threeid UNIQUEIDENTIFIER, oldval UNIQUEIDENTIFIER) 

    INSERT #tempone SELECT NEWID(), oneid FROM #one WHERE oneid = @guid 
    INSERT #temptwo SELECT NEWID(), #tempone.oneid, #two.twoid FROM #two JOIN #tempone ON #two.oneid = #tempone.oldval 
    INSERT #tempthree SELECT NEWID(), #temptwo.twoid, #three.threeid FROM #three JOIN #temptwo ON #three.twoid = #temptwo.oldval 
    INSERT #tempfour SELECT NEWID(), #tempthree.threeid, #four.fourid FROM #four JOIN #tempthree ON #four.threeid = #tempthree.oldval 

-- INSERT results 
    INSERT #one SELECT t.oneid /*#one.column_list*/ FROM #tempone t JOIN #one oldT ON t.oldval = oldT.oneid 
    INSERT #two SELECT t.twoid, t.oneid /*#two.column_list*/ FROM #temptwo t JOIN #two oldT ON t.oldval = oldT.twoid 
    INSERT #three SELECT t.threeid, t.twoid /*#three.column_list*/ FROM #tempthree t JOIN #three oldT ON t.oldval = oldT.threeid  
    INSERT #four SELECT t.fourid, t.threeid /*#four.column_list*/ FROM #tempfour t JOIN #four oldT ON t.oldval = oldT.fourid  

-- View Results 
    SELECT one.oneid, two.twoid, three.threeid, four.fourid 
    FROM #one one 
    JOIN #two two ON one.oneid = two.oneid 
    JOIN #three three on three.twoid = two.twoid 
    JOIN #four four on four.threeid = three.threeid 
    ORDER BY one.oneid, two.twoid, three.threeid, four.fourid 
5

如果你的PK IDENTITY列,你可以使用涉及了在this question描述MERGE的技術。

下面是整個過程可能腳本:

DECLARE @OldID int, @NewID int; 
SET @OldID = some_value; 

DECLARE @TwoMapping TABLE (OldID int, NewID int); 
DECLARE @ThreeMapping TABLE (OldID int, NewID int); 

INSERT INTO One 
SELECT columns 
FROM One 
WHERE OneID = @OldID; 
SET @NewID = SCOPE_IDENTITY(); 
/* 
That one was simple: one row is copied, so just reading SCOPE_IDENTITY() 
after the INSERT. The actual mapping technique starts at this point. 
*/ 

MERGE Two tgt 
USING (
    SELECT 
    @NewID AS OneID, 
    other columns 
    FROM Two t 
    WHERE OneID = @OldID 
) src 
ON 0 = 1 
WHEN NOT MATCHED THEN 
    INSERT (columns) VALUES (src.columns) 
OUTPUT src.TwoID, INSERTED.TwoID INTO @TwoMapping (OldID, NewID); 
/* 
As you can see, MERGE allows us to reference the source table in the 
OUTPUT clause, in addition to the pseudo-tables INSERTED and DELETED, 
and that is a great advantage over INSERT and the core of the method. 
*/ 

MERGE Three tgt 
USING (
    SELECT 
    map.NewID AS TwoID, 
    t.other columns 
    FROM Three t 
    INNER JOIN @TwoMapping map ON t.TwoID = map.OldID 
) src 
ON 0 = 1 
WHEN NOT MATCHED THEN 
    INSERT (columns) VALUES (src.columns) 
OUTPUT src.ThreeID, INSERTED.ThreeID INTO @ThreeMapping (OldID, NewID); 
/* 
Now that we've got a mapping table, we can easily substitute new FKs for the old 
ones with a simple join. The same is repeated once again in the following MERGE. 
*/ 

MERGE Four tgt 
USING (
    SELECT 
    map.NewID AS ThreeID, 
    t.columns 
    FROM Four t 
    INNER JOIN @ThreeMapping map ON t.ThreeID = map.OldID 
) src 
ON 0 = 1 
WHEN NOT MATCHED THEN 
    INSERT (columns) VALUES (src.columns); 
/* 
The Four table is the last one in the chain of dependencies, so the last MERGE 
has no OUTPUT clause. But if there were a Five table, we would go on like above. 
*/ 

或者你可能需要使用遊標,這似乎是在SQL Server 2005中這樣做的唯一(理智)的方式和更早的版本。