使用merge..output获取source.id和target.id之间的映射
非常简单,我有两个表来源和目标。
declare @Source table (SourceID int identity(1,2), SourceName varchar(50)) declare @Target table (TargetID int identity(2,2), TargetName varchar(50)) insert into @Source values ('Row 1'), ('Row 2')
我想将所有行从@Source
移动到@Target
并知道每个SourceID
的TargetID
,因为还有需要复制的表SourceChild
和TargetChild
,我需要将新的TargetID
添加到TargetChild.TargetID
FK列。
这有几个解决scheme。
- 使用while循环或游标一次向目标插入一行(RBAR),并使用
scope_identity()
填充TargetChild
的FK。 - 将一个临时列添加到
@Target
并插入SourceID
。 然后您可以join该列以获取TargetID
中的FK的TargetChild
。 - 为
@Target
SET IDENTITY_INSERT OFF
,并@Target
处理分配新值。 你得到一个范围,然后你在TargetChild.TargetID
使用。
我并不都喜欢他们中的任何一个。 我到目前为止使用的是游标。
我真正想做的是使用insert语句的output
子句。
insert into @Target(TargetName) output inserted.TargetID, S.SourceID select SourceName from @Source as S
但这是不可能的
The multi-part identifier "S.SourceID" could not be bound.
但是,这是可能的合并。
merge @Target as T using @Source as S on 0=1 when not matched then insert (TargetName) values (SourceName) output inserted.TargetID, S.SourceID;
结果
TargetID SourceID ----------- ----------- 2 1 4 3
我想知道你是否使用过这个? 如果您对解决scheme有任何想法或看到有任何问题? 它在简单的场景下工作正常,但当查询计划由于复杂的源查询而变得非常复杂时,可能会发生一些难看的事情。 最糟糕的情况是TargetID / SourceID对实际上不匹配。
MSDN有这个关于输出子句的from_table_name
的说法。
是一个列前缀,用于指定包含在用于指定要更新或删除的行的DELETE,UPDATE或MERGE语句的FROM子句中的表。
出于某种原因,他们不会说“行插入,更新或删除”只有“行更新或删除”。
任何想法是受欢迎的,完全不同的解决scheme,原来的问题,非常感谢。
在我看来,这是对MERGE和输出的一个很好的运用。 我已经在几个场景中使用过,至今还没有经历过任何古怪的事情。 例如,这里是testing设置,将其中的一个文件夹和所有文件(标识)克隆到一个新创build的文件夹(guid)中。
DECLARE @FolderIndex TABLE (FolderId UNIQUEIDENTIFIER PRIMARY KEY, FolderName varchar(25)); INSERT INTO @FolderIndex (FolderId, FolderName) VALUES(newid(), 'OriginalFolder'); DECLARE @FileIndex TABLE (FileId int identity(1,1) PRIMARY KEY, FileName varchar(10)); INSERT INTO @FileIndex (FileName) VALUES('test.txt'); DECLARE @FileFolder TABLE (FolderId UNIQUEIDENTIFIER, FileId int, PRIMARY KEY(FolderId, FileId)); INSERT INTO @FileFolder (FolderId, FileId) SELECT FolderId, FileId FROM @FolderIndex CROSS JOIN @FileIndex; -- just to illustrate DECLARE @sFolder TABLE (FromFolderId UNIQUEIDENTIFIER, ToFolderId UNIQUEIDENTIFIER); DECLARE @sFile TABLE (FromFileId int, ToFileId int); -- copy Folder Structure MERGE @FolderIndex fi USING ( SELECT 1 [Dummy], FolderId, FolderName FROM @FolderIndex [fi] WHERE FolderName = 'OriginalFolder' ) d ON d.Dummy = 0 WHEN NOT MATCHED THEN INSERT (FolderId, FolderName) VALUES (newid(), 'copy_'+FolderName) OUTPUT d.FolderId, INSERTED.FolderId INTO @sFolder (FromFolderId, toFolderId); -- copy File structure MERGE @FileIndex fi USING ( SELECT 1 [Dummy], fi.FileId, fi.[FileName] FROM @FileIndex fi INNER JOIN @FileFolder fm ON fi.FileId = fm.FileId INNER JOIN @FolderIndex fo ON fm.FolderId = fo.FolderId WHERE fo.FolderName = 'OriginalFolder' ) d ON d.Dummy = 0 WHEN NOT MATCHED THEN INSERT ([FileName]) VALUES ([FileName]) OUTPUT d.FileId, INSERTED.FileId INTO @sFile (FromFileId, toFileId); -- link new files to Folders INSERT INTO @FileFolder (FileId, FolderId) SELECT sfi.toFileId, sfo.toFolderId FROM @FileFolder fm INNER JOIN @sFile sfi ON fm.FileId = sfi.FromFileId INNER JOIN @sFolder sfo ON fm.FolderId = sfo.FromFolderId -- return SELECT * FROM @FileIndex fi JOIN @FileFolder ff ON fi.FileId = ff.FileId JOIN @FolderIndex fo ON ff.FolderId = fo.FolderId
我想添加另一个例子来增加@ Nathan的例子,因为我发现它有点混乱。
矿大部分使用真正的表,而不是临时表。
我也从这里得到了灵感: 另一个例子
-- Copy the FormSectionInstance DECLARE @FormSectionInstanceTable TABLE(OldFormSectionInstanceId INT, NewFormSectionInstanceId INT) ;MERGE INTO [dbo].[FormSectionInstance] USING ( SELECT fsi.FormSectionInstanceId [OldFormSectionInstanceId] , @NewFormHeaderId [NewFormHeaderId] , fsi.FormSectionId , fsi.IsClone , @UserId [NewCreatedByUserId] , GETDATE() NewCreatedDate , @UserId [NewUpdatedByUserId] , GETDATE() NewUpdatedDate FROM [dbo].[FormSectionInstance] fsi WHERE fsi.[FormHeaderId] = @FormHeaderId ) tblSource ON 1=0 -- use always false condition WHEN NOT MATCHED THEN INSERT ( [FormHeaderId], FormSectionId, IsClone, CreatedByUserId, CreatedDate, UpdatedByUserId, UpdatedDate) VALUES( [NewFormHeaderId], FormSectionId, IsClone, NewCreatedByUserId, NewCreatedDate, NewUpdatedByUserId, NewUpdatedDate) OUTPUT tblSource.[OldFormSectionInstanceId], INSERTED.FormSectionInstanceId INTO @FormSectionInstanceTable(OldFormSectionInstanceId, NewFormSectionInstanceId); -- Copy the FormDetail INSERT INTO [dbo].[FormDetail] (FormHeaderId, FormFieldId, FormSectionInstanceId, IsOther, Value, CreatedByUserId, CreatedDate, UpdatedByUserId, UpdatedDate) SELECT @NewFormHeaderId, FormFieldId, fsit.NewFormSectionInstanceId, IsOther, Value, @UserId, CreatedDate, @UserId, UpdatedDate FROM [dbo].[FormDetail] fd INNER JOIN @FormSectionInstanceTable fsit ON fsit.OldFormSectionInstanceId = fd.FormSectionInstanceId WHERE [FormHeaderId] = @FormHeaderId