1

我以前从来没有在 StackOverflow 上发布过问题,因为我总是可以通过搜索在这里找到答案。只是这一次,我想我有一个真正的笨蛋......


我正在编写代码来自动化将数据从一个 SQL Server 数据库移动到另一个数据库的过程。我有一些非常标准的 SQL Server 数据库,它们的一些表之间具有外键关系。直截了当的东西。我的要求之一是需要一举复制整个表,而不是遍历行或使用游标。另一个要求是我必须在 SQL 中执行此操作,没有 SSIS 或其他外部助手。

例如:

INSERT INTO TargetDatabase.dbo.MasterTable 
SELECT * FROM SourceDatabase.dbo.MasterTable

这很容易。然后,一旦移动了 MasterTable 中的数据,我就会移动子表的数据。

INSERT INTO TargetDatabase.dbo.ChildTable 
SELECT * FROM SourceDatabase.dbo.ChildTable

当然,实际上我使用更明确的 SQL……就像我专门命名所有字段和类似的东西,但这只是一个简化版本。无论如何,到目前为止一切都很好,除了......

问题是主表的主键被定义为一个身份字段。因此,当我插入 MasterTable 时,新表的主键由数据库计算。因此,为了解决这个问题,我尝试使用 OUTPUT INTO 语句将更新的值放入 Temp 表中:

INSERT INTO TargetDatabase.dbo.MasterTable 
OUPUT INSERTED.* INTO @MyTempTable
SELECT * FROM SourceDatabase.dbo.MasterTable

所以这就是一切崩溃的地方。由于数据库更改了主键,我到底如何确定临时表中的哪条记录与源表中的原始记录匹配?

你看到问题了吗?我知道新 ID 是什么,只是不知道如何可靠地将它与原始记录匹配。SQL 服务器允许我输出 INSERTED 值,但不允许我在 INSERTED 值旁边输出 FROM TABLE 值。我用触发器试过了,我用SP试过了,总是有同样的问题。

如果我一次只更新一条记录,我可以轻松地将我的 INSERTED 值与我试图插入的原始记录匹配以查看旧主键值和新主键值,但我有这个要求批量执行。

有任何想法吗?

PS:我不允许更改目标或源表的表结构。

4

2 回答 2

3

您可以使用MERGE

declare @Source table (SourceID int identity(1,2), SourceName varchar(50))
declare @Target table (TargetID int identity(2,2), TargetName varchar(50))

insert into @Source values ('Row 1'), ('Row 2')

merge @Target as T
using @Source as S
on 0=1
when not matched then
  insert (TargetName) values (SourceName)
output inserted.TargetID, S.SourceID;

结果:

TargetID    SourceID
----------- -----------
2           1
4           3

Adam Machanic 在这篇博文中介绍:输出博士或:我如何学会停止担心并爱上 MERGE

于 2012-07-28T08:03:37.803 回答
0

为了说明我在评论中提到的内容:

SET IDENTITY_INSERT TargetDatabase.dbo.MasterTable  ON

INSERT INTO TargetDatabase.dbo.MasterTable  (IdentityColumn, OtherColumn1, OtherColumn2, ...)
SELECT IdentityColumn, OtherColumn1, OtherColumn2, ... 
FROM SourceDatabase.dbo.MasterTable  

SET IDENTITY_INSERT TargetDatabase.dbo.MasterTable  OFF

好的,因为这对您不起作用(目标表中的预先存在的值),如何为两个表中的 id 值添加一个固定增量(偏移量)(使用当前的最大 id 值)。假设两个表中的标识列都是“id”:

DECLARE @incr int
BEGIN TRAN
SELECT @incr = max(id)
FROM TargetDatabase.dbo.MasterTable AS m WITH (TABLOCKX, HOLDLOCK)

SET IDENTITY_INSERT TargetDatabase.dbo.MasterTable ON

INSERT INTO TargetDatabase.dbo.MasterTable (id{, othercolumns...})
SELECT id+@incr{, othercolumns...}
FROM SourceDatabase.dbo.MasterTable

SET IDENTITY_INSERT TargetDatabase.dbo.MasterTable OFF

INSERT INTO TargetDatabase.dbo.ChildTable (id{, othercolumns...})
SELECT id+@incr{, othercolumns...} 
FROM SourceDatabase.dbo.ChildTable

COMMIT TRAN
于 2012-07-27T22:46:53.403 回答