将关系数据从数据库复制到数据库

时间:2008-09-12 15:42:58

标签: sql sql-server sql-server-2005 .net-3.5

编辑:让我完全改写一下,因为我不确定是否有像我最初描述的XML方式。

又一个编辑:这需要是一个可重复的过程,并且必须能够以可以在C#代码中调用的方式进行设置。

在数据库A中,我有一组与PK和FK相关的表。一个父表,包括子表和孙表,比如说。

我想将一组行从数据库A复制到数据库B ,它具有相同名称的表和字段。对于每个表,我想插入数据库B中的同一个表中。但我不能限制使用相同的主键。 复制例程必须为数据库B中的每一行创建新的PK ,并且必须将这些PK传播到子行。换句话说,我在数据之间保持相同的关系,但不是完全相同的PK和FK。

你会如何解决这个问题?我愿意接受建议。 SSIS并没有被完全排除,但它并不像我看起来那样做。我也对LINQ中的解决方案,或者使用类型化的DataSet,或者使用某些XML,或者只是在SQL Server 2005和/或C#(.NET 3.5)中可以使用的任何东西开放。最好的解决方案不需要SSIS,也不需要编写大量代码。但我会承认,这种“最佳”解决方案可能不存在。

(我没有自己完成这项任务,也没有限制;这就是给我的方式。)

11 个答案:

答案 0 :(得分:2)

我认为SQL Server实用程序tablediff.exe可能就是您要找的。

另见this thread

答案 1 :(得分:1)

首先,我要说SSIS是你最好的选择。但是,要回答你问的问题......

我不相信你可以随时创建新的id,尽管你可以,但你需要将原始ID用于查找。

您可以获得的最好的是表的一个插入语句。下面是一个代码示例,用于执行SELECT以获取XML示例中的数据:

declare @xml xml 
set @xml='<People Key="1" FirstName="Bob" LastName="Smith">
  <PeopleAddresses PeopleKey="1" AddressesKey="1">
    <Addresses Key="1" Street="123 Main" City="St Louis" State="MO" ZIP="12345" />
  </PeopleAddresses>
</People>
<People Key="2" FirstName="Harry" LastName="Jones">
  <PeopleAddresses PeopleKey="2" AddressesKey="2">
    <Addresses Key="2" Street="555 E 5th St" City="Chicago" State="IL" ZIP="23456" />
  </PeopleAddresses>
</People>
<People Key="3" FirstName="Sally" LastName="Smith">
  <PeopleAddresses PeopleKey="3" AddressesKey="1">
    <Addresses Key="1" Street="123 Main" City="St Louis" State="MO" ZIP="12345" />
  </PeopleAddresses>
</People>
<People Key="4" FirstName="Sara" LastName="Jones">
  <PeopleAddresses PeopleKey="4" AddressesKey="2">
    <Addresses Key="2" Street="555 E 5th St" City="Chicago" State="IL" ZIP="23456" />
  </PeopleAddresses>
</People>
'

select t.b.value('./@Key', 'int') PeopleKey,
    t.b.value('./@FirstName', 'nvarchar(50)') FirstName,
    t.b.value('./@LastName', 'nvarchar(50)') LastName
from @xml.nodes('//People') t(b)

select t.b.value('../../@Key', 'int') PeopleKey,
    t.b.value('./@Street', 'nvarchar(50)') Street,
    t.b.value('./@City', 'nvarchar(50)') City,
    t.b.value('./@State', 'char(2)') [State],
    t.b.value('./@Zip', 'char(5)') Zip
from 
@xml.nodes('//Addresses') t(b)

这样做是从XML中获取节点并解析数据。为了获得人们的关系id,我们使用../../来上链。

答案 2 :(得分:0)

转储XML方法并使用导入向导/ SSIS。

答案 3 :(得分:0)

到目前为止,最简单的方法是Red Gate的SQL数据比较。您可以将其设置为在一两分钟内完成您所描述的内容。

答案 4 :(得分:0)

我也喜欢Red Gate的SQL Compare和Data Compare,但据我所知,它不会满足他对更改主键的要求。

如果跨数据库查询/链接服务器是一个选项,您可以使用存储过程来执行此操作,该存储过程将DB A中父/子的记录复制到DB B上的临时表中,然后在新的主键中添加一个列。插入标题后要更新的临时子表。

我的问题是,如果记录没有相同的主键,您如何判断它是否是新记录?还有其他候选人钥匙吗?如果这些是新表,为什么他们不能拥有相同的主键?

答案 5 :(得分:0)

我用一组存储过程创建了相同的东西。

数据库B将拥有自己的主键,但存储数据库A的主键,用于进行调试。这意味着我可以拥有多个数据库A!

通过链接服务器复制数据。不太快; SSIS更快。但是SSIS不适合初学者,编写适用于更改源表的代码并不容易。

从C#调用存储过程很容易。

答案 6 :(得分:0)

我在存储过程中编写脚本,使用Inserts来完成艰苦的工作。您的代码将从表A中获取PK(可能是通过@@ Scope_Identity) - 我假设表A的PK是一个标识字段?

你可以使用临时表,游标,或者你可能更喜欢使用CLR - 它可能适合这种操作。

我很惊讶地发现一种工具可以用a)预先确定的键或b)标识字段来实现这一点(显然表B和C没有它们)。

答案 7 :(得分:0)

您是每次清除目的地表然后重新开始吗?这将对您需要实施的解决方案产生重大影响。如果您每次都进行完全重新导入,那么您可以执行以下操作:

创建临时表或表变量以记录父表的旧主键和新主键。

将父表数据插入目标并使用OUTPUT子句捕获新ID,并将旧ID插入临时表。 注意:使用output子句是有效的,允许您批量插入而不循环遍历要插入的每个记录。

插入子表数据。加入临时表以检索所需的新外键。

上述过程可以使用T-SQL Script,C#代码或SSIS完成。我倾向于SSIS。

答案 8 :(得分:0)

如果每次添加,则可能需要保留永久表以跟踪源数据库主键和目标数据库主键之间的关系(至少对于父表)。如果您需要将此类数据保留在目标数据库之外,您可以让SSIS从某种日志数据库甚至是平面文件中存储/检索它。

如果父表中存在可用于唯一标识该记录的字段组合,因此可以避免上述情况,从而“找到”目标数据库中该记录的主键。

答案 9 :(得分:0)

我认为我将要使用的最有可能的类型是数据集。它不是一个普遍的解决方案;如果任何表发生变化,我们将不得不重新生成它们。但根据我所说的,这不是问题;表格预计不会有太大变化。

数据集将使分层循环数据并在插入后从数据库刷新PK变得相当容易。

答案 10 :(得分:0)

在处理类似任务时,我只是创建了一组存储过程来完成这项工作。

由于您指定的任务非常自定义,因此您不太可能找到“随时可用”的解决方案。

只是给你一些提示:

  • 如果数据库位于不同的服务器上,则使用链接服务器,以便您只需通过TSQL访问源表和目标表

在存储过程中:

  • 确定需要复制的父项 - 您说主键是不同的,因此您需要使用唯一约束(如果表已规范化,您应该能够定义它们)
  • 根据已识别的父项确定需要复制的子项,以检查其中是否有一些已经在目标数据库中再次使用唯一约束方法
  • 识别孙子项目(与父子项目相同的逻辑)
  • 从最低级别(孙子,孩子,父母)开始复制数据

不需要游标等,只需将临时结果存储在临时表中(如果在一个存储过程中工作,则存储表变量)

这种方法对我很有用。

您当然可以将参数添加到主存储过程,这样您就可以复制所有新记录,也可以只复制您指定的记录。

如果有任何帮助,请告诉我。