【发布时间】: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