【问题标题】:What could be the workaround to avoid the MERGE issue i.e. The target of a MERGE statement cannot be a remote table?避免 MERGE 问题的解决方法是什么,即 MERGE 语句的目标不能是远程表?
【发布时间】:2021-11-14 05:07:43
【问题描述】:

在不同的服务器上将数据从一个表复制到另一个表,但结构相似。

结束了。

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 语句的目标不能是远程表、远程视图或远程表上的视图。

解决方法可能是反向的,即目标和服务器,但这在我的情况下并不理想。

我该怎么办?

原问题:

Original question

这样做的原因:

原因是我正在复制父子数据,并且在目标中对父的引用将丢失,因为主键是自动生成的,因此在目标中,父中的新记录会生成新的 Id,但子会具有作为源的旧父 ID,因此丢失。因此,为避免合并将确保 tyo 使用新的父 ID 更新子记录。

编辑:

newDB 位于不同的服务器上,即[192.168.xxx.xxx].newDB.dbo.tblStudents

【问题讨论】:

  • 原因是我正在复制父子数据并且在目标中对父的引用会丢失,因为主键是自动生成的,因此在目标中父中的新记录会生成新的 Id 但是child 将具有源的旧父 ID,因此丢失。因此,为避免合并将确保 tyo 使用新的父 ID 更新子记录。
  • 好的。 newDB 位于不同的服务器上,即 [192.168.xxx.xxx].newDB.dbo.tblStudents
  • @RobbieRobertson SQL Server 不支持您所要求的(跨服务器 MERGE 和 DDL),但即使它是链接表和链接服务器也不适合该用途 -案子。您实际上想做什么?如果您想在两个 SQL Server 实例之间同步数据,那么您应该使用适当的复制功能,而不是尝试使用MERGE 破解它。如果您想将数据从一台服务器移动到另一台服务器,请使用 SSIS。
  • @Dai 谢谢。避免复制的原因是因为它是有条件的,即数据只能在少数表之间复制,并且只有在满足某些条件(即状态发生变化)时,一些布尔值为真假,因此在这种情况下复制不是很理想,因为它只有 2表。
  • 尽管我很讨厌它,但答案可能实际上是 SSIS。您可以连接到远程数据库并一次插入一条记录,使用SCOPE_IDENTITY() 捕获结果并将它们写入其他地方。我真的不是 SSIS 的粉丝,但在这种情况下,这似乎是你唯一的选择。

标签: sql sql-server tsql linked-server


【解决方案1】:

如果您无法更改远程数据库结构,我建议直接在目标类表中构建 ClassId 映射表:

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;

这个脚本的问题或风险在于它不是幂等的——执行两次它会产生重复。 为了消除这种风险,有必要以某种方式在源端记住已经插入的内容。

【讨论】:

  • 嗨@vadim:谢谢哥们。让我试着告诉你。
猜你喜欢
  • 2023-03-05
  • 2018-08-22
  • 1970-01-01
  • 2022-11-10
  • 2010-11-28
  • 1970-01-01
  • 1970-01-01
  • 2012-06-28
  • 1970-01-01
相关资源
最近更新 更多