避免 MERGE 问题的解决方法是什么,即 MERGE 语句的目标不能是远程 table?
What could be the workaround to avoid the MERGE issue i.e. The target of a MERGE statement cannot be a remote table?
将数据从一个 table 复制到另一个都在不同的服务器上,但结构相似。
就此结束。
declare @ClassIds table (OldClassId int, NewClassId int);
merge into newDB.dbo.tblClasses as target
using
(
select
Id = Id * (-1),
[Name]
from
oldDB.dbo.tblClasses
)
as source on source.Id = target.Id
when not matched by target then
insert ([Name])
values (source.[Name])
output source.Id * (-1), inserted.Id -- ← the trick is here
into @ClassIds (OldClassId, NewClassId);
insert into newDB.dbo.tblStudents
select
s.Id,
s.[Name],
ClassId = ids.NewClassId
from
oldDB.dbo.tblStudents s
inner join @ClassIds ids on ids.OldClassId = s.ClassId;
但是错误:
MERGE 语句的目标不能是远程 table、远程视图或远程 tables 上的视图。
解决方法可能是颠倒的,即目标和服务器,但这在我的情况下并不理想。
我该怎么办?
原题:
这样做的原因:
原因是我正在复制父子数据,在目标中,对父项的引用将丢失,因为主键是自动生成的,因此在目标中,父项中的新记录将生成新的Id but child 将拥有源的旧父 ID,因此丢失。因此,为避免合并将确保您使用新的父 ID 更新子记录。
编辑:
newDB 在不同的服务器上,即 [192.168.xxx.xxx].newDB.dbo.tblStudents
如果您无法更改远程数据库结构,我建议在目标 Class table 中构建 ClassId 映射 table:
drop table if exists #ClassIdMap;
create table #ClassIdMap (SourceClassId int, TargetClassId int);
declare @Prefix varchar(10) = 'MyClassId=';
insert into targetServer.targetDb.dbo.Classes
([Name])
select
-- insert the source class id values with some unique prefix
[Name] = concat(@Prefix, Id)
from
sourceServer.sourceDb.dbo.Classes;
-- then create the ClassId mapping table
-- getting the SourceClassId by from the target Name column
insert #ClassIdMap (
SourceClassId,
TargetClassId)
select
SourceClassId = replace([Name], @Prefix, ''),
TargetClassId = Id
from
targetServer.targetDb.dbo.Class
where
[Name] like @Prefix + '%';
-- replace the source Ids with the Name values
update target set
[Name] = source.[Name]
from
targetServer.targetDb.dbo.Class target
inner join #ClassIdMap map on map.TargetClassId = target.Id
inner join sourceServer.sourceDb.dbo.Classes source on source.Id = map.SourceClassId;
-- and use the ClassId mapping table
-- to insert Students into correct classes
insert into targetServer.targetDb.dbo.Students (
[Name] ,
ClassId )
select
s.[Name],
ClassId = map.TargetClassId
from
sourceServer.sourceDb.dbo.Students s
inner join #ClassIdMap map on map.SourceClassId = s.ClassId;
此脚本的问题或风险在于它不是幂等的 — 执行两次会创建重复项。
为了消除这种风险,有必要以某种方式记住源端已经插入的内容。
将数据从一个 table 复制到另一个都在不同的服务器上,但结构相似。
就此结束。
declare @ClassIds table (OldClassId int, NewClassId int);
merge into newDB.dbo.tblClasses as target
using
(
select
Id = Id * (-1),
[Name]
from
oldDB.dbo.tblClasses
)
as source on source.Id = target.Id
when not matched by target then
insert ([Name])
values (source.[Name])
output source.Id * (-1), inserted.Id -- ← the trick is here
into @ClassIds (OldClassId, NewClassId);
insert into newDB.dbo.tblStudents
select
s.Id,
s.[Name],
ClassId = ids.NewClassId
from
oldDB.dbo.tblStudents s
inner join @ClassIds ids on ids.OldClassId = s.ClassId;
但是错误:
MERGE 语句的目标不能是远程 table、远程视图或远程 tables 上的视图。
解决方法可能是颠倒的,即目标和服务器,但这在我的情况下并不理想。
我该怎么办?
原题:
这样做的原因:
原因是我正在复制父子数据,在目标中,对父项的引用将丢失,因为主键是自动生成的,因此在目标中,父项中的新记录将生成新的Id but child 将拥有源的旧父 ID,因此丢失。因此,为避免合并将确保您使用新的父 ID 更新子记录。
编辑:
newDB 在不同的服务器上,即 [192.168.xxx.xxx].newDB.dbo.tblStudents
如果您无法更改远程数据库结构,我建议在目标 Class table 中构建 ClassId 映射 table:
drop table if exists #ClassIdMap;
create table #ClassIdMap (SourceClassId int, TargetClassId int);
declare @Prefix varchar(10) = 'MyClassId=';
insert into targetServer.targetDb.dbo.Classes
([Name])
select
-- insert the source class id values with some unique prefix
[Name] = concat(@Prefix, Id)
from
sourceServer.sourceDb.dbo.Classes;
-- then create the ClassId mapping table
-- getting the SourceClassId by from the target Name column
insert #ClassIdMap (
SourceClassId,
TargetClassId)
select
SourceClassId = replace([Name], @Prefix, ''),
TargetClassId = Id
from
targetServer.targetDb.dbo.Class
where
[Name] like @Prefix + '%';
-- replace the source Ids with the Name values
update target set
[Name] = source.[Name]
from
targetServer.targetDb.dbo.Class target
inner join #ClassIdMap map on map.TargetClassId = target.Id
inner join sourceServer.sourceDb.dbo.Classes source on source.Id = map.SourceClassId;
-- and use the ClassId mapping table
-- to insert Students into correct classes
insert into targetServer.targetDb.dbo.Students (
[Name] ,
ClassId )
select
s.[Name],
ClassId = map.TargetClassId
from
sourceServer.sourceDb.dbo.Students s
inner join #ClassIdMap map on map.SourceClassId = s.ClassId;
此脚本的问题或风险在于它不是幂等的 — 执行两次会创建重复项。 为了消除这种风险,有必要以某种方式记住源端已经插入的内容。