1

我有一个基于实体框架的完整生产站点,现在我需要每周将大量数据导入数据库。数据以文本文件的形式出现,我逐行查看,检查数据库以查看它是否存在以及它是否更新了任何已更改的内容,或者如果没有则插入它。我遇到的问题是运行完整的导入过程大约需要 32 小时,并且必须手动将一些文件拆分成更小的块,以避免看似由实体框架引起的内存问题。我设法减慢了内存增加的速度,但上次我运行一个文件而不拆分它时,它运行了大约 12 个小时,然后在超过 1.5gb 的地方耗尽了内存。所以有人可以向我建议导入这些数据的最佳方式,我听说过 sqlbulkcopy,但不确定它是否正确使用。谁能提供任何例子?或提出更合适的建议。例如,我是否应该使用标准 .net sql 命令创建实体的副本并可能使用存储过程

4

2 回答 2

2

尽管 SqlBulkCopy 对托管代码很方便,但我认为最快的方法是在“纯”sql 中执行此操作 - 鉴于 SqlBulkCopy 不容易执行 upserts,无论如何您都需要执行下面的 MERGE 部分

假设您的文本文件是 csv 格式,并且它在 SQL Server 上以“C:\Data\TheFile.txt”的形式存在,并且行尾被规范化为 CR-LF (\r\n)

假设数据是 ID,Value1,Value2

此 SQL 命令将插入到具有兼容数据类型的 ID、Value、Value2 列的暂存表 TheFile_Staging 中,然后更新“真实”表 TheFile_Table(注意:下面的代码未经测试!)

  truncate table TheFile_Staging
    BULK INSERT TheFile_Staging FROM'C:\Data\TheFile.txt'
 WITH (fieldterminator=',', rowTerminator='\r\n',FirstRow=2)
  //FirstRow=2 means skip Row#1 - use this when 1st row is a header.

MERGE TheFile_Table as target
USING (SELECT ID,Value1,Value2 from TheFile_Staging) as source
on target.ID = source.ID
WHEN MATCHED THEN
  UPDATE SET target.Value1=source.Value1, target.Value2=source.target2
WHEN NOT MATCHED THEN 
  INSERT (id,Value1,Value2) VALUES (source.Id,source.Value1,source.Value2);

您可以创建一个存储过程并将其设置为运行或从代码调用等。这种方法的唯一问题是错误处理批量插入有点混乱 - 但只要您的数据输入正常,那么它就足够了快速地。

通常我会在 WHERE 子句中添加某种验证检查,我们使用 MERGE 的 USING() 选择来仅获取在数据方面有效的行。

可能还值得指出的是,暂存表的定义应该省略任何非空、主键和身份约束,以便可以毫无错误地读取数据,尤其是。如果您的源数据中到处都有空字段;而且我通常也更喜欢将日期/时间数据作为纯 nvarchar 提取 - 这样可以避免格式错误的日期导致导入错误,并且您的 MERGE 语句可以根据需要执行 CAST 或 CONVERT,同时忽略和/或记录到错误表中遇到的任何无效数据。

于 2013-09-05T22:56:49.827 回答
0

可悲的是,在这种情况下,您需要远离实体框架;开箱即用 EF 仅进行逐行插入。您可以做类似这样的有趣事情,或者您可以完全忽略 EF 并手动编写将使用 ADO.Net (SqlBulkCopy) 进行批量插入的类。

编辑:如果性能可以接受,您也可以使用当前方法,但您需要定期重新创建上下文,而不是对所有记录使用相同的上下文。我怀疑这就是内存消耗过大的原因。

于 2013-09-05T12:45:44.577 回答