【问题标题】:C# Fast Upload Related TablesC#快速上传相关表
【发布时间】:2018-02-19 23:33:54
【问题描述】:

我正在开发一个动态加载器。基于定义平面文本文件的数据库表,我可以读取具有多种记录类型的单个文件并将其加载到数据库表中。这些表是相关的并使用身份主键。目前一切正常,但运行速度非常慢,因为这一切都是由单个插入语句完成的。我正在努力优化流程,但在网络上找不到“简单”或“最佳实践”的答案。

我当前的项目处理 8 个表格,但为了简化,我将使用客户/订单示例。

让我们看看下面的两个客户,数据将针对数据文件中的每组客户和订单重复。父记录总是在子记录之前。第一个字段是记录类型,每个记录类型对后面的字段都有不同的定义。这都是在控制表中指定的。

CUST|Joe Green|123 Main St
ORD|Pancakes|5
ORD|Nails|2
CUST|John Deere|456 Park Pl
ORD|Tires|4

当前代码将:

  • 插入客户 Joe Green 并返回一个 ID。 (使用输出 插入语句中的 Inserted.Id)
  • 插入附有返回 ID 的煎饼和钉子订单。
  • 插入客户 John Deere 并返回一个 ID。
  • 插入带有退货 ID 的订单轮胎。

这运行得非常缓慢。如果这可以优化并且我不必更改太多代码,那将是理想的,但我想不出如何。

那么解决方案呢?我在考虑数据表...这是我目前所想的。

  • 创建交易
  • 锁定属于“文件定义”的所有表,在这种情况下 客户和订单获取每个表的最大 ID 并递增 1 拥有所有表的起始 ID
  • 为所有表创建数据表
  • 按照当前设置执行,而不是发出插入语句 添加到数据表
  • 读取数据后以正确的顺序批量上传表,基于 关系
  • 解锁表
  • 结束交易

在我走这条路之前,我想知道是否有人提出了更好的解决方案。我也在考虑在 SSIS 中使用自定义脚本组件。我看过关于推迟提交事务的帖子和博客,但是每个父记录只有几个子记录,并且树最多可以达到 4 层,想想订单详细信息和产品。由于需要父记录 ID,我需要提交父记录的插入。我也考虑过自己管理 ID 而不是身份,但如果可以避免的话,我不想添加额外的管理。

更新基于答案,以澄清/上下文。

一个典型的文本文件有

one file header record
- 5 facility records that relate to the file header
- 7,000 customers(account)
- 5 - 10 notes per customer
- 1-5 payments at the account level
- 1-5 adjustments at the account level
- 5 - 20 orders per customer
- 5 - 20 order details per order
- 1-5 payments at the order level
- 1-5 adjustments at the order level
- one file trailer record related to the file header



Keys
- File Header -> Facility -> Customer (Account)
- File Header -> FileTrailer
- Customer -> Notes
- Customer -> Payments
- Customer -> Adjustments
- Customer -> Orders
- Order -> OrderDetails
- Order -> Payments
- Order -> Adjustments

还涉及到一些表格,但这应该可以让您了解整体上下文。

Data Sample ... = MORE FIELDS .... MORE RECORDS
HEADER|F1|F2|...
FACILITY|F1|F2|..
CUSTOMER|F1|F2|...
NOTE|F1|F2|....
....
ORDER|F1|F2|...
ORDERDETAIL|F1|F2|...
.... ORDER DETAILS
ORDERPYMT|F1|F2|...
....
ORDERADJ|F1|F2|...
....
CUSTOMERPYMT|F1|F2|...
....
CUSTOMERADJ|F1|F2|...
....

(The structure repeats for each facility)

TRAILER|F1|F2|...

【问题讨论】:

  • 您为什么确信需要提交才能从父表中获取记录 ID?
  • 几个月前和你的情况非常相似,尝试了所有的导入方法(从 Excel/CSV 的 RBAR,常规 SQL 连接等),最终选择了sqlbulkcopy。作为该过程的一部分,有时需要添加一列。为了解决这个问题,我使用新定义创建了一个新表(使用 SMO,但这不相关),从旧表中插入行,从新文本文件中插入行,重命名新表,重命名旧表。当然还有其他方法,但这比单独插入要快得多(尤其是在定义发生变化的情况下)。
  • @user6144226 我需要保留要在订单插入中使用的客户 ID。如果我不提交客户插入,我如何确保另一个进程不会执行插入并获取该客户 ID?
  • @JacobH 我的问题是,当我构建数据表时,我需要管理父记录 ID 的保留。如果我需要在相关表上进行多次批量插入,我看不到该怎么做。
  • 因为当您插入具有标识的表时,会消耗该值。即使您将它放在事务中并将其回滚,它也会被消耗。这里的事务似乎只增加了复杂性和可能的​​性能损失(尽管对于这么小的事务不太可能)。

标签: c# sql sql-server


【解决方案1】:

插入具有低数据量的相关表通常不成问题。如果速度较慢,我们将需要更多上下文来回答您的问题。

如果您因为要插入许多记录而遇到问题,您可能需要查看SqlBulkCopy
如果您不想自己管理 id,我所知道的最简洁的方法是使用临时占位符 id 列。

  • 使用您自己填写的数据和 tempId 列以及外键空白创建和填充数据表
  • SqlBulkCopy 主表
  • 通过 tempids 列从先前插入的表中查找主键,使用生成的外键更新辅助数据表
  • 上传辅助表
  • 重复直到完成
  • 删除临时 ID 列(可选)

【讨论】:

  • 上下文是每个客户都有一些订单,每个订单都有一些订单详细信息。每个订单都有一些注释。每个订单都有一些付款。对于每笔付款,都有一对多的笔记。现在想象一下 50000 个客户。必须提交要保留的每个父密钥会导致进程运行非常缓慢。
  • 每个实例要插入的行数最多的表的平均 to-insert-rowcount 是多少?我们是在说 100 个,10 000 个吗?
  • 您所描述的数字看起来会从所描述的方法中受益。看到您还必须解析一堆 CSV 数据,我会考虑使用 SSIS,但自 2012 年以来我没有使用过,所以我无法帮助您。
猜你喜欢
  • 2020-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-21
  • 2021-12-30
  • 1970-01-01
  • 2013-07-06
  • 1970-01-01
相关资源
最近更新 更多