通过 C# 导入 Excel 文件时的性能瓶颈

Posted

技术标签:

【中文标题】通过 C# 导入 Excel 文件时的性能瓶颈【英文标题】:Performance bottleneck while importing a Excel file via C# 【发布时间】:2020-08-29 18:42:17 【问题描述】:

我收到了一项任务,要将一个 Excel 文件 (xlsx) 导入数据库,该文件由第三方(我们对结构、名称和内容没有影响)创建。该文件包括几张具有非标准工作表名称的工作表(而不是“Sheet1”或“数据”或具有诸如“1 2 grp 1 & 2 包括测试的数据”之类的名称 - 仅用于结构描述的虚拟名称)。我必须从这个文件中导入一张特定的表格,其中包含大约 4000 行和 205 列。该文件的大小约为 20 MB。在这 205 列中,只有 65 列(不是在工作表中按顺序)必须导入(类似于 col 1-4、10-16、50-100,...)。

我尝试通过 OleDB 连接实现此导入,但我收到一个错误,即工作表名称无效(我猜它连接到 & 和 . 并且可能是名称中的空格...)。

现在我通过Excel.WorkbookExcel.Range 实现了导入,并通过xlRange.Cells[i, colId].Value2.ToString() 获取相应的单元格。首先,我遍历范围内第一行的所有列,以获得列的名称。这样做时,我检查该列是否包含在 65 个必需列的列表中,如果是这种情况,我将列 ID 和名称添加到数据表中。在下一步中,我会遍历工作表中的所有行,并且对于每个工作表,我都会遍历我的数据表,以便在所需的列中获取单元格的数据。

看起来,这不是一个好主意:我在 25 分钟后取消了该过程(只是循环遍历行和列;当时没有数据处理/插入或其他任何事情)。此外,我注意到,在我取消进程之前,Excel hat 使用的内存已超过 200 MB。

是否有更高效的方法来仅从所需列中获取数据?

从数据库的角度来看,4000 行并不是那么大,但我猜 205 列可能会导致问题,导致总共 820k 单元格......似乎通过数据表过滤的想法没有'没有预期的减少工作量的效果。

【问题讨论】:

这些都是插页吗?在我的脑海中,您可以 1)可能更改工作表的名称,然后通过 OleDB 进行导入 2)将整个工作表读入二维数组并循环遍历 3)如果它们都是插入,则使用批量插入或复制 @Kevin 到目前为止没有插入 - 暂时只是循环以获取所有行和所有必需的列。但是,是的,将来所有记录都必须插入数据库。如前所述,205 列中只有 65 列是相关的。那么加载整个 4000x205 阵列可以工作吗?从未将这种大小的 Excel 文件完全加载到内存中(并且不知道如何在不循环列和行索引的情况下实现它)。 @Kevin 重命名不是一个选项 - 文件将被推送到一个目录并且进程应该自动导入它 我本人不是该主题的专家(恰恰相反),但您是否考虑过 Open XML SDK?看到这个,它可能会有所帮助:docs.microsoft.com/en-us/office/open-xml/… 就我个人而言,我会尝试批量加载整个工作表,然后删除我不想要的任何内容。在 SQL 中执行此操作通常比在 Excel 中快得多。如果格式永远不会改变,那么这将变成一个简单的 EXCEL -> 转储表 -> 清理表操作。如果您可以自动将您真正想要的单张工作表保存为 CSV,那就更好了。 【参考方案1】:

经过长时间的反复试验,我终于设法让 OleDB 查询在 Excel 工作表上运行。正如我在另一个线程中读到的(甚至猜测另一个网站),如果工作表名称(或列名称)包含点,则 OleDB 查询将不起作用。这些将有 - 在查询期间,而不是在文件本身 - 被 # 替换,因此工作表名称 value a & b incl. test 的查询将如下所示:SELECT [col1], col[2]... FROM [value a & b incl# test$]

【讨论】:

以上是关于通过 C# 导入 Excel 文件时的性能瓶颈的主要内容,如果未能解决你的问题,请参考以下文章

如何找到 C# 桌面应用程序的性能瓶颈?

将 7z 文件提取到目录 c# 时的性能问题

用JFR和JMC分析SolrCloud集群性能瓶颈

使用 ADO 将大型 csv 文件导入 mdb 时的性能问题

C# - 提高搜索时的性能

将 Rgb 图像转换为灰度 C# 代码时的性能问题