非常简化,我有两个表Source和Target。
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并TargetID为每个行知道,SourceID因为还有表SourceChild,TargetChild也需要复制这些表,并且需要将新行添加TargetID到TargetChild.TargetIDFK列中。
有两种解决方案。
使用while循环或游标一次将一行(RBAR)插入到Target并用于scope_identity()填充的FK TargetChild。 将临时列添加到@Target并插入SourceID。然后,您可以加入该列以在中获取TargetIDFK TargetChild。 SET IDENTITY_INSERT OFF为@Target自己处理新值。您将得到一个范围,然后在中使用TargetChild.TargetID。 我并不喜欢它们中的任何一个。到目前为止,我使用的是游标。
scope_identity()
FK TargetChild
TargetIDFK TargetChild
SET IDENTITY_INSERT OFF为@Target
我真正想做的是使用outputinsert语句的子句。
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
我想知道您是否使用过?如果您对解决方案有任何想法或发现任何问题?在简单的场景中它可以正常工作,但是当由于复杂的源查询而使查询计划变得非常复杂时,可能会发生一些丑陋的事情。最糟糕的情况是TargetID / SourceID对实际上不匹配。
MSDN有这样说的from_table_name了的输出条款。
是列前缀,用于指定DELETE,UPDATE或MERGE语句的FROM子句中包含的表,该表用于指定要更新或删除的行。
由于某些原因,他们不会说“要插入,更新或删除的行”,而只会说“要更新或删除的行”。
欢迎任何想法,非常感谢您对原始问题有完全不同的解决方案。
我认为这是MERGE和输出的一种很好的用法。我已经在几种情况下使用过,到目前为止还没有遇到过任何奇怪的事情。例如,这是一个测试设置,它将一个文件夹及其中的所有文件(身份)克隆到一个新创建的文件夹(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