2010-10-04 26 views
5

cơ sở dữ liệu của tôi trông như thế này:T-SQL: Cách tốt nhất để sao chép dữ liệu phân cấp?

Questionnaire 
Id 
Description 

Category 
id 
description 
QuestionnaireId (FK)  

Question 
id 
CategoryId (FK) 
field 

Khi tôi sao chép một bảng câu hỏi, tôi muốn sao chép tất cả các bảng bên dưới. Vì vậy, điều này có nghĩa là bảng Câu hỏi sẽ nhận được một Id mới. Sau đó, tất cả các thể loại thuộc bảng câu hỏi cũng phải được sao chép. Vì vậy, các danh mục mới được chèn phải có Id câu hỏi mới. Sau các loại, các câu hỏi phải được sao chép. Nhưng categoryId phải được cập nhật vào danh mục mới được chèn vào.

Làm cách nào tôi có thể thực hiện việc này bằng t-sql?

+3

Tôi thực sự không nhìn thấy vấn đề ở đây ... chỉ cần sao chép các dữ liệu và chèn sử dụng 'SET IDENTITY_INSERT [bảng A] ON' tương tự cho B & C. – Jamiec

+0

Nếu tôi chèn một bản ghi trong bảng A, bản ghi này nhận được một ID, giả sử 104. Hơn nữa, tôi phải chèn một hàng mới trong bảng B, có tham chiếu đến tableA, vì vậy table_A_id phải giống nhau: 104 – Martijn

+1

Không nên một vấn đề miễn là bất cứ nơi nào bạn đang sao chép là trống, vì dữ liệu mới sẽ có cùng một PK bằng cách sử dụng IDENTITY_INSERT. Bạn đang cố sao chép vào đâu? Một cơ sở dữ liệu giống hệt nhau có lẽ? – Jamiec

Trả lời

5

Điều này khá dễ thực hiện, nhưng bạn phải theo dõi mọi thứ khi bạn thực hiện. Tôi thường sẽ tạo ra một SP duy nhất cho điều này, đó là một đầu vào bảng câu hỏi để sao chép.

DECLARE @newQuestionnaireId INT 
    INSERT INTO Questionnaire 
    (Id,Description) 
    SELECT Id, Description 
    FROM Questionnaire 
    WHERE ID = @sourceQuestionnaireID 
    SET @newquestionnaireId = SCOPE_IDENTITY() 

Tại thời điểm này bạn có bản ghi đầu trang mới và Id mới được tạo cho bản sao. Bước tiếp theo là để tải các loại vào một bảng tạm thời trong đó có một trường bổ sung cho Id

DECLARE @tempCategories TABLE (id INT, description VARCHAR(50),newId INT) 
INSERT INTO @tempCategories(id,description) 
SELECT id, description FROM Category 
WHERE questionnaireId = @sourceQuestionnaireId 

mới Bây giờ, bạn có một bảng tạm thời với tất cả các loại để chèn, cùng với một trường để che lấp các mới ID cho danh mục này. Sử dụng con trỏ để đi qua danh sách chèn bản ghi mới và sử dụng cuộc gọi SCOPE_IDENTITY tương tự để chèn lấp Id mới.

DECLARE cuCategory CURSOR FOR SELECT Id, Description FROM @tempCategories 
DECLARE @catId INT, @catDescription, @newCatId INT 
OPEN cuCategory 
FETCH NEXT FROM cuCategory INTO @catId,@catDescription 
WHILE @@FETCH_STATUS<>0 
BEGIN 
    INSERT INTO Category(description,questionnaireId) 
    VALUES(@catDescription,@newQuestionnaireId) 
    SET @newCatId = SCOPE_IDENTITY() 

    UPDATE @tempCategories SET [email protected] 
    WHERE [email protected] 
    FETCH NEXT FROM cuCategory INTO @catId,@catDescription 
END 
CLOSE cuCategory 
DEALLOCATE cuCategory 

Tại thời điểm này, bạn có bảng tạm thời ánh xạ catId từ bảng câu hỏi gốc đến catId cho bảng câu hỏi mới. Điều này có thể được sử dụng để điền vào bảng cuối cùng theo cùng một cách - mà tôi sẽ để lại như là một bài tập cho bạn, nhưng cảm thấy tự do để gửi lại ở đây nếu bạn gặp khó khăn.

Cuối cùng, tôi sẽ đề nghị rằng toàn bộ hoạt động này được thực hiện trong một giao dịch để giúp bạn tiết kiệm từ một nửa bản sao hoàn thành khi xảy ra sự cố.

Một vài tuyên bố từ chối trách nhiệm: Ở trên tất cả đều được nhập nhanh chóng, không mong đợi nó hoạt động tốt. Thứ hai, Ive giả định rằng tất cả các PK của bạn là các trường nhận dạng, mà chúng nên là! Nếu chúng không chỉ thay thế các cuộc gọi SCOPE_IDENTITY() với logic thích hợp để tạo ID tiếp theo.

Edit: documentation for Cursor operations can be foundhere

+0

Cảm ơn bạn. Đây thực sự là những gì tôi cần. Tôi thấy rất nhiều thứ mới mà tôi chưa từng làm việc với: CURSOR, FETCH NEXT FROM, FETCH_STATUS. Bạn có thể giải thích những điều này một chút? – Martijn

+3

Tôi thực tế đã viết câu trả lời cho bạn và bạn không thể gõ vài cụm từ đó vào Google? Sheesh. – Jamiec

+0

Tôi nghĩ rằng tôi nhận được nó :) Thnx! – Martijn

2

Tôi đã có một vấn đề như thế này và bắt đầu thực hiện các giải pháp được đề xuất bởi @Jamiec nhưng tôi nhanh chóng nhận ra rằng tôi cần một giải pháp tốt hơn bởi vì mô hình của tôi là lớn hơn nhiều so với trong ví dụ trích dẫn ở đây . Tôi có một bảng chính với ba bảng trung gian, mỗi bảng có một hoặc nhiều bảng đại học. Và ba trung gian mỗi cái có 50 cột. Điều này sẽ có nghĩa là rất nhiều công việc để loại tất cả lên, đặc biệt là trong phần lấy với các memvars tạm thời. Tôi đã cố gắng tìm một cách để FETCH trực tiếp vào bảng tạm thời nhưng có vẻ như bạn không thể làm điều đó. Điều tôi đã làm là thêm cột vào các bảng trung gian có tên là OriginalId. Đây là mã của tôi được dịch sang mô hình được người hỏi sử dụng:

DECLARE @newQuestionnaireId INT 
INSERT INTO Questionnaire (Id,Description) 
SELECT Id, Description FROM Questionnaire 
WHERE ID = @sourceQuestionnaireID 
SET @newquestionnaireId = SCOPE_IDENTITY() 

INSERT INTO Category(QuestionnaireId, description, originalId) 
SELECT @newquestionnaireId, description, id FROM Category 
WHERE questionnaireId = @sourceQuestionnaireId 

INSERT INTO Question SELECT Category.Id, Question.Field 
FROM Question join Category on Question.CategoryId = Category.OriginalId 
WHERE Category.QuestionnaireId = @newquestionnaireId 

Trong mô hình của tôi, trường id là tất cả các danh tính, do đó bạn không cung cấp chúng trong chèn.

Một điều tôi phát hiện ra trước khi tôi từ bỏ cách tiếp cận CURSOR là this clever little trick để tránh phải gõ lệnh FETCH hai lần bằng cách sử dụng một vòng lặp while vô hạn với một BREAK:

1

đây là một cách mà không có con trỏ , nó dựa vào việc nhớ thứ tự của các sự kiện, và sau đó sử dụng nó để giải quyết các em.

Declare @Parrent TABLE(ID int PRIMARY KEY IDENTITY, Value nvarchar(50)) 
Declare @Child TABLE(ID int PRIMARY KEY IDENTITY, ParrentID int, Value nvarchar(50)) 

insert into @Parrent (Value) Values ('foo'),('bar'),('bob') 
insert into @Child (ParrentID, Value) Values (1,'foo-1'),(1,'foo-2'),(2,'bar-1'),(2,'bar-2'),(3,'bob') 

declare @parrentToCopy table (ID int) -- you can me this a collection 
insert into @parrentToCopy values (2) 

select * from @Parrent p inner join @Child c on p.ID = c.ParrentID order by p.ID asc, c.ID asc 

DECLARE @Ids TABLE(nID INT); 

INSERT INTO @Parrent (Value) 
OUTPUT INSERTED.ID 
INTO @Ids 
SELECT 
     Value 
FROM @Parrent p 
inner join @parrentToCopy pc on pc.ID=p.ID 
ORDER BY p.ID ASC 

INSERT INTO @Child (ParrentID, Value) 
SELECT 
     nID 
     ,Value 
FROM @Child c 
inner join (select ID, ROW_NUMBER() OVER (ORDER BY ID ASC) AS 'RowNumber' from @parrentToCopy) o ON o.ID = c.ParrentID 
inner join (select nID, ROW_NUMBER() OVER (ORDER BY nID ASC) AS 'RowNumber' from @Ids) n ON o.RowNumber = n.RowNumber 

select * from @Parrent p inner join @Child c on p.ID = c.ParrentID order by p.ID asc, c.ID asc 

bài đầy đủ là đây http://bashamer.wordpress.com/2011/10/04/copying-hierarchical-data-in-sql-server/

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