【问题标题】:How to copy parent and child data from one database to another while assigning the newly created parent ids in the foreign key column of child tables?如何在子表的外键列中分配新创建的父ID时,将父子数据从一个数据库复制到另一个数据库?
【发布时间】:2021-12-23 17:39:03
【问题描述】:

假设,

我有一张桌子tblClassestblStudents

现在每个班级都有多名学生。

tblClass

ID Name
1  ClassA
2  ClassB
3  ClassC

tbl学生

ID  Name  Class_ID
1.  john   1
2.  Mathew 1
3.  Imran  2
4.  Jenny  3

现在,我有另一台服务器具有完全相同的 db 和表,我正在使用 SelectInsert 将数据从同一表从 server 1 复制到 server 2,例如

insert into server2.dbo.tblClass (Name)
select Name from server1.dbo.tblClass

对于tblStudents

insert into server2.dbo.tblStudents (Name, Class_ID)
select Name, Class_ID from server1.dbo.tblStudents 

现在这没问题,但真正的问题是在server2 复制数据后,如何用插入数据后生成的tblClass 的实际IDs 填充tblStudents fk Class_ID进入server2 中的tblStudents,因为 PK 是标识和自动递增的,不能更改设计。

在这种情况下该怎么办?

简单来说,当复制父数据和子数据时,在子表中,外键字段需要填充父的实际 ID,而不是复制它的位置,这显然是不同的。

我不允许更改表格设计或属性,必须使用查询来完成。

有什么建议吗?

【问题讨论】:

    标签: sql sql-server tsql stored-procedures


    【解决方案1】:

    方法是在插入类记录时创建一个ClassId 映射表,并使用该映射表将OldClassId 转换为NewClassId 用于新的Student 表:

    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 语句不仅可以使用inserteddeleted 列(如INSERTUPDATEDELETE 语句那样),还可以使用source 列也是。

    【讨论】:

    • 让我试试这个,谢谢,会告诉你
    • 伙计们,这里的这个人,@vadim,是国王。伙计,很棒,很棒的工作。我希望我能有你这样的想法。完成。干得好。
    • 鉴于您只有when not matched,您可以将ON 条件更改为1=0。还有*-1 是关于什么的,为什么有必要这样做?
    • 哦,出现了 1 个问题。它说“MERGE 语句的目标不能是远程表、远程视图或远程表上的视图。”
    • @Charlieface 是的,关于1=0ON 条件下的优点——这也是一个选项。第一个我的*(-1) 是使Ids 为负数,因此它们不匹配任何目标Ids,第二个我的*(-1) 是将Ids 返回为正数。
    【解决方案2】:

    我能想到的方法有两种:

    首先是使用SET IDENTITY_INSERT tblClass ON。这不是设计更改,因此您应该能够做到。之后,您可以在 tblClass.ID 中插入您自己的值(尽管您需要选择列表括号):

    insert tblClass(ID,Name) values (1, 'ClassA')....
    

    或者,您可以进行查询,将学生 ID 连接到班级名称,然后使用该查询创建相应的连接:

    -- export/save this in temp table
    select s.Name,c.Name as className
    into #a
    from tblStudents s
    left join tblClass c on s.Class_ID=c.ID
    
    --now use this to fill db2 tblStudents
    insert tblStudents(Name,Class_ID)
        select #a.Name,c.ID
        from 
            #a
            inner join tblClass c on #a.className=c.Name
    

    【讨论】:

    • 对于第二个解决方案,我们可以有许多同名的学生。对于第一种解决方案;如果 id 已经存在怎么办?
    • 如果类 id 已经存在,则不能使用第一个解决方案。关于您的另一个问题,学生姓名无关紧要-您之前有三个约翰,您将再次拥有三个约翰。但是两个 Class ID 不应该共享相同的名称,否则会导致问题。
    • 不幸的是,我们有同名但 id 不同的类:(
    • 如果你有两个名为“Math”的类,它们在 db 1 中的 id 为 1 和 2,在 db 2 中的 id 为 8 和 9,你怎么知道连接?
    • @Robbie Robertson 幸好你的问题已经解决了。不要觉得有义务感谢每个人或为不接受答案而道歉——我们都知道游戏在这里是如何进行的。在 Stack Overflow 上玩得开心:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    • 1970-01-01
    相关资源
    最近更新 更多