使用merge..output获取source.id和target.id之间的映射

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并且需要复制,我需要将新行添加TargetIDTargetChild.TargetIDFK列中.

有几个解决方案.

  1. 使用while循环或游标一次向Target插入一行(RBAR)并使用scope_identity()填充FK TargetChild.
  2. 添加临时列@Target并插入SourceID.然后,您可以加入该列以获取TargetIDFK中的内容TargetChild.
  3. SET IDENTITY_INSERT OFFfor @Target和处理自己分配新值.您将获得一个然后使用的范围TargetChild.TargetID.

我不是那么喜欢他们中的任何人.我到目前为止使用的是游标.

我真正想要做的是使用outputinsert语句的子句.

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)

  • 有趣的是,这对于实现复制行和一组子行的简单任务来说似乎格外复杂......实际上游标实际上不易理解吗? (4认同)