我的数据库如下所示:
Questionnaire
Id
Description
Category
id
description
QuestionnaireId (FK)
Question
id
CategoryId (FK)
field
当我复制问卷时,我想复制所有基础表格。所以这意味着表调查表得到一个新的Id。然后,还必须复制调查问卷的所有所有类别。因此,新插入的类别必须获得新的问卷ID。在类别之后,必须复制问题。但必须将categoryId更新为新插入的类别。
如何使用t-sql执行此操作?
答案 0 :(得分:8)
这很容易实现,但你必须随时跟踪所有内容。我通常会为此创建一个SP,它将问卷作为输入进行复制。
DECLARE @newQuestionnaireId INT
INSERT INTO Questionnaire
(Id,Description)
SELECT Id, Description
FROM Questionnaire
WHERE ID = @sourceQuestionnaireID
SET @newquestionnaireId = SCOPE_IDENTITY()
此时您有一个新的标题记录,以及新生成的副本ID。下一步是将类别加载到临时表中,该临时表具有新Id的额外字段
DECLARE @tempCategories TABLE (id INT, description VARCHAR(50),newId INT)
INSERT INTO @tempCategories(id,description)
SELECT id, description FROM Category
WHERE questionnaireId = @sourceQuestionnaireId
现在,您有一个临时表,其中包含要插入的所有类别,以及用于回填此类别的新ID的字段。使用光标覆盖插入新记录的列表,并使用类似的SCOPE_IDENTITY调用来回填新Id。
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 newCatId=@newCatId
WHERE id=@catId
FETCH NEXT FROM cuCategory INTO @catId,@catDescription
END
CLOSE cuCategory
DEALLOCATE cuCategory
此时您现在有一个临时表,它将catId从原始问卷映射到新问卷的catId。这可以用来填写决赛桌的方式大致相同 - 我将为你留下一个练习,但如果你有困难,可以随时回复。
最后,我建议整个操作在一次交易中进行,以便在出现问题时将您从半完成的副本中拯救出来。
一些免责声明:以上所有都是快速输入,不要指望它能够起作用。其次,我认为你所有的PK都是身份字段,它们应该是!如果他们不只是用适当的逻辑替换SCOPE_IDENTITY()调用来生成下一个ID。
答案 1 :(得分:2)
我有这样的问题,并开始实施@Jamiec建议的解决方案,但我很快意识到我需要一个更好的解决方案,因为我的模型比这里引用的例子大得多。我有一个包含三个中间表的主表,每个表都有一个或多个第三表。这三种中间体各有50列。这将意味着要输入所有内容的大量工作,特别是在带有临时memvars的获取部分中。我试图找到一种方法将FETCH直接导入临时表,但似乎你不能这样做。 我所做的是在名为OriginalId的中间表中添加一列。这是我的代码翻译成提问者使用的模型:
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
在我的模型中,id字段都是Identities,因此您不会在插入中提供它们。
我在放弃CURSOR方法之前发现的另一件事是this clever little trick,以避免必须使用带有BREAK的无限WHILE循环来输入两次FETCH语句:
答案 2 :(得分:1)
这是一种没有游标的方法,它依赖于记住事件的顺序,然后使用它来解决孩子。
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
完整帖子在这里http://bashamer.wordpress.com/2011/10/04/copying-hierarchical-data-in-sql-server/