如何将父子数据从一个数据库复制到另一个数据库,同时在子表的外键列中分配新创建的父 ID?

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?

假设,

我有一个 table tblClassestblStudents

现在每个 class 有多个学生。

tblClass

ID Name
1  ClassA
2  ClassB
3  ClassC

tblStudents

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

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

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 复制数据后,如何用 IDs 的实际 IDs 填充 tblStudents fk Class_ID tblClass 是在 server2 中将数据插入 tblStudents 后生成的,因为 PK 是 Identity 且自动递增,无法更改设计。

遇到这种情况怎么办?

简而言之,当复制父数据和子数据时,子数据 table 外键字段需要填充父数据的实际 ID,而不是复制数据的来源会明显不同。

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

有什么建议吗?

我认为有两种方法:

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

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

或者,您可以进行查询,将学生 ID 连接到 class names,然后使用它来创建相应的连接:

-- 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

方法是在 class 记录插入上创建一个 ClassId 映射 table 并使用此映射 table 将 OldClassId 转换为 NewClassId 新学生 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 语句不仅适用于 inserteddeleted 列(如 INSERTUPDATEDELETE 语句)但也有 source 列。