Mik*_*son 60 sql-server merge sql-server-2008
很简单,我有两个表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')
Run Code Online (Sandbox Code Playgroud)
我想将所有行移动@Source
到@Target
并知道TargetID
每个行,SourceID
因为还有表格SourceChild
,TargetChild
并且需要复制,我需要将新行添加TargetID
到TargetChild.TargetID
FK列中.
有几个解决方案.
scope_identity()
填充FK TargetChild
.@Target
并插入SourceID
.然后,您可以加入该列以获取TargetID
FK中的内容TargetChild
.SET IDENTITY_INSERT OFF
for @Target
和处理自己分配新值.您将获得一个然后使用的范围TargetChild.TargetID
.我不是那么喜欢他们中的任何人.我到目前为止使用的是游标.
我真正想要做的是使用output
insert语句的子句.
insert into @Target(TargetName)
output inserted.TargetID, S.SourceID
select SourceName
from @Source as S
Run Code Online (Sandbox Code Playgroud)
但这是不可能的
The multi-part identifier "S.SourceID" could not be bound.
Run Code Online (Sandbox Code Playgroud)
但合并是可能的.
merge @Target as T
using @Source as S
on 0=1
when not matched then
insert (TargetName) values (SourceName)
output inserted.TargetID, S.SourceID;
Run Code Online (Sandbox Code Playgroud)
结果
TargetID SourceID
----------- -----------
2 1
4 3
Run Code Online (Sandbox Code Playgroud)
我想知道你是否使用过它?如果您对该解决方案有任何想法或有任何问题吗?它在简单的场景中工作正常,但是当查询计划由于复杂的源查询而变得非常复杂时,可能会发生丑陋的事情.最糟糕的情况是TargetID/SourceID对实际上不匹配.
MSDN from_table_name
对输出子句有这个说法.
是一个列前缀,用于指定DELETE,UPDATE或MERGE语句的FROM子句中包含的表,该语句用于指定要更新或删除的行.
出于某种原因,他们没有说"要插入,更新或删除的行"只有"要更新或删除的行".
欢迎任何想法,并且非常感谢对原始问题的完全不同的解决方案.
Nat*_*erl 44
在我看来,这是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
Run Code Online (Sandbox Code Playgroud)