【问题标题】:SqlBulkCopy Error handling / continue on errorSqlBulkCopy 错误处理/错误继续
【发布时间】:2009-06-17 00:13:34
【问题描述】:

我正在尝试将大量数据插入 SQL 服务器。我的目标表有一个名为“Hash”的唯一索引。

我想用 SqlBulkCopy 替换我的 SqlDataAdapter 实现。在 SqlDataAapter 中有一个名为“ContinueUpdateOnError”的属性,当设置为 true 适配器时。Update(table) 将插入所有可能的行并使用 RowError 属性标记错误行。

问题是如何使用 SqlBulkCopy 尽可能快地插入数据,同时跟踪哪些行被插入,哪些行没有被插入(由于唯一索引)?

这里是附加信息:

  1. 该过程是迭代的,通常按计划重复。

  2. 源表和目标表可能很大,有时有数百万行。

  3. 尽管可以先检查散列值,但每行需要两个事务(首先从目标表中选择散列,然后执行插入)。我认为在 adapter.update(table) 的情况下,检查 RowError 比检查每行的哈希命中要快。

【问题讨论】:

  • 为什么不先从要插入的数据集中删除已经存在的行,然后再尝试插入?
  • ChrisW,感谢您的建议。如果您建议我执行此查询“从表中删除 (value1, value2, ..) 中的哈希值。我不能使用这种方法,因为它会将每一行都视为目标表中不存在,但是我系统的其他部分只喜欢处理新记录(没有哈希冲突的记录)。
  • @Paladin,查看扩展答案

标签: c# ado.net sqlbulkcopy


【解决方案1】:

SqlBulkCopy 的错误处理功能非常有限,默认情况下它甚至不检查约束。

但是,它很快,非常非常快。

如果您想解决重复键问题,并确定批次中哪些行是重复的。一种选择是:

  • 开始翻译
  • 在表上抓取一个 tabblockx 选择所有当前的“哈希”值并将它们放入一个哈希集中。
  • 过滤掉重复项并报告。
  • 插入数据
  • 提交翻译

如果您要插入大量集合并且表中初始数据的大小不是太大,则此过程将有效。

能否请您扩展您的问题以包括问题的其余内容。

编辑

现在我有了更多的上下文,你可以用另一种方法来解决它:

  • 将批量插入临时表。
  • 开始可序列化的翻译
  • 选择目标表中已经存在的所有临时行...报告它们
  • 将临时表中的数据插入到真实表中,对散列执行左连接并包括所有新行。
  • 提交翻译

这个过程在往返过程中非常轻松,并且考虑到您的规格最终应该非常快;

【讨论】:

  • 我真的需要这个解决方案,但我不是高级 SQL 用户,也不了解可序列化 tran 的工作原理。你能帮忙理解一下吗?
【解决方案2】:

与已经建议的方法略有不同;执行SqlBulkCopy 并捕获抛出的SqlException

    Violation of PRIMARY KEY constraint 'PK_MyPK'. Cannot insert duplicate 
key in object 'dbo.MyTable'. **The duplicate key value is (17)**.

然后,您可以从 ID 17(重复的第一条记录)中删除来源中的所有项目。我在这里做出的假设适用于我的情况,可能不适用于你的情况;即,由于上传过程中的 SQL/网络错误,重复是由先前失败的 SqlBulkCopy 中的完全相同相同的数据引起的。

【讨论】:

    【解决方案3】:

    注意:这是对 Sam 的回答的回顾,其中包含更多细节

    感谢 Sam 的回答。由于评论的空间限制,我已将其放在答案中。

    根据您的回答,我看到了两种可能的方法:

    解决方案一:

    • 开始翻译
    • 通过执行“select hash in destinationtable where hash in (val1, val2, ...) 来获取所有可能的命中“哈希”值
    • 过滤掉重复并报告
    • 插入数据
    • 提交翻译

    解决方案2:

    • 创建临时表以镜像 目标表的架构
    • 批量插入临时表
    • 启动可序列化事务
    • 获取重复行:“选择哈希来自 临时表在哪里 tempTable.hash=destinationTable.hash"
    • 报告重复行
    • 在临时表中插入数据 进入目标表:“select * into destinationTable from temptable left join temptable.hash=destinationTable.hash where destinationTable.hash is null”
    • 提交翻译

    既然我们有两种方法,那么归根结底是哪种方法最优化?两种方法都必须检索重复的行并报告,而第二种方法需要额外的:

    • 临时表的创建和删除
    • 另外一个用于将数据从临时表移动到目标表的 sql 命令
    • 取决于哈希冲突的百分比,它还会通过网络传输大量不必要的数据

    如果这些是唯一的解决方案,那么在我看来,第一种方法会胜出。你们有什么感想?谢谢!

    【讨论】:

    • 圣骑士,你误解了第二种方法。它不涉及将完整的哈希列表拉到客户端,而是在服务器上执行重复分析。这使得它可以更好地扩展一个预先存在的表已经有大量数据。
    • Sam,你是在说“获取重复行:”从 tempTable 中选择哈希,其中 tempTable.hash=destinationTable.hash“”这一步吗?此步骤不会将完整的哈希列表拉到客户端,只是我在客户端需要的匹配/冲突哈希。它确实“选择目标表中已经存在的所有临时行......报告它们”。您能否详细说明重复分析过程?谢谢!
    猜你喜欢
    • 2015-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-17
    • 2012-02-22
    相关资源
    最近更新 更多