Find Failed row/rows on SqlBulkCopy.WriteToServer exception 并通过忽略那些失败的行来重试 SqlBulkCopy

Find Failed row/rows on SqlBulkCopy.WriteToServer exception and retrySqlBulkCopy by omitting those rows that failed

我正在尝试批量插入对象列表(发票列表)。有时它会失败并抛出异常。但是,我想知道哪些行失败了。这样我就可以通过省略这些行来重做批量插入。我可以这样做吗?

这是使用 批量 操作的缺点之一,反馈是全部或 none 种响应。

When we use Bulk operations we are generally bypassing the validation and verification that would normally take place for each individual row, that is the main trade-off to improve performance. You can't have both.

SqlBulkCopy 特意设计用于经过清理的数据

因此,在尝试复制数据之前,您应该首先考虑如何清理数据,这可以采取多种形式,因此我们无法在本文中涵盖所有内容 post。

可能失败的最常见约束是空值(在不支持空值的字段中)和外键(空值或不匹配)。 通常我们可以pre-validate空值和不存在的键的批量数据,只需查询您的批量集以在不支持空值的列中找到具有空值的行。您还可以查询外键列中的值在目标数据库中尚不存在的任何行。

You have not provided any detail on the structure of your data that you have in memory, so this is an abstract example for filtering based on nulls:

DataTable bulkData;
... load the data
// Columns that do not support nulls: Col1, Col3
DataRow[] dataWithNulls = bulkData.Select("[Col1] IS NULL OR [Col3] IS NULL");
// Get only rows that do not have nulls
DataRow[] bulkDataSanitised = bulkData.Select("[Col1] IS NOT NULL AND [Col3] IS NOT NULL");

To do a similar query on missing FKs you must first get a distinct list of the FK values, then query the DB to find those values that do not have a match, then you can filter out the rows that have those missing FK values.

如果您是从一般的角度来处理这个问题,所以您事先不知道 table 模式,那么我们通常在这种情况下遵循的概念过程是将批量集分解为更小的块并执行这些块。

在您的界面中,允许用户指定起始行和要复制的行数,如果可行,请从源集中删除行。如果失败,请用户重试。


您最后的选择是完全不批量执行此操作!您仍然可以使用 SqlBulkCopy,但是一次只能发送一行,这允许您在失败时处理每一行。

如果您出于性能原因使用 SqlBulkCopy(当然还有其他 non-performance 使用 SqlBulkCopy 的原因) 那么所有性能都会丢失如果您使用此方法,但是如果失败频率较低,则首先尝试完整的批量操作,然后在失败时逐行执行。

这篇关于代码项目的文章 Retrieving failed records after an SqlBulkCopy exception 解释了一个解决方案来帮助解决这个问题,但您应该很容易想出自己的实施方案。


您可以结合这两种方法,首先尝试全部,然后在失败时将 table 拆分为多个子 table,然后继续递归尝试然后拆分tables 直到你到达第 1 行的 tables。这类似于用户如何手动完成相同的消除过程,并且仍然会保留一些性能优势,而不是从一开始就逐行进行,但这仅适用于故障率相对较低的大型集。