如何将父子数据从一个数据库复制到另一个数据库,同时在子表的外键列中分配新创建的父 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 tblClasses
和 tblStudents
现在每个 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
使用 Select
和 Insert
例如
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
语句不仅适用于 inserted
和 deleted
列(如 INSERT
、UPDATE
、DELETE
语句)但也有 source
列。
假设,
我有一个 table tblClasses
和 tblStudents
现在每个 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
使用 Select
和 Insert
例如
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
语句不仅适用于 inserted
和 deleted
列(如 INSERT
、UPDATE
、DELETE
语句)但也有 source
列。