【问题标题】:Writing to Excel file containing formulas is extremely slow写入包含公式的 Excel 文件非常慢
【发布时间】:2011-12-27 14:17:50
【问题描述】:

我们有一个自动流程,可以打开模板 Excel 文件,写入数据行,然后将文件返回给用户。这个过程通常很快,但是我最近被要求在其中一个模板中添加一个包含一些 Excel 公式的摘要页面,现在这个过程需要很长时间。

几分钟后它成功运行了大约 5 条记录,但是本周的记录集几乎是 400 行,而我让它运行的最长大约是半小时后取消它。如果没有公式,只需几秒钟即可运行。

将行写入包含公式的 Excel 文件是否存在任何已知问题?或者有没有办法告诉 Excel 在用户打开文件之前不要计算公式?

汇总表上的公式如下:

' Returns count of cells in column where data = Y
=COUNTIF(Sheet1!J15:Sheet1!J10000, "Y") 
=COUNTIF(Sheet1!F15:Sheet1!F10000, "Y")

' Return sum of column where data is a number greater than 0
' Column contains formula calculating the difference in months between two dates
=SUMIF(Sheet1!I15:Sheet1!I10000,">0",Sheet1!I15:Sheet1!I10000)  

' Returns a count of distinct values in a column
=SUMPRODUCT((Sheet1!D15:Sheet1!D10000<>"")/COUNTIF(Sheet1!D15:Sheet1!D10000,Sheet1!D15:Sheet1!D10000&""))

写入 excel 的代码如下所示:

Dim xls as New Excel.Application()
Dim xlsBooks as Excel.Workbooks, xlsBook as Excel.Workbook
Dim xlsSheets as Excel.Sheets, xlsSheet as Excel.Worksheet
Dim xlsCells as Excel.Range

xls.Visible = False
xls.DisplayAlerts = False

xlsBooks = xls.Workbooks

xlsBooks.Open(templateFile)
xlsBook = xlsBooks.Item(1)  

' Loop through excel Sheets. Some templates have multiple sheets.
For Each drSheet as DataRow in dtSheets.Rows
    xlsSheets = xlsBook.Worksheets
    xlsSheet = CType(xlsSheets.Item(drSheet("SheetName")), Excel.Worksheet)
    xlsCells = xlsSheet.Cells

    ' Loop though Column list from Database. Each Template requires different columns
    For Each drDataCols as DataRow in dtDataCols.Rows

        ' Loop though Rows to get data
        For Each drData as DataRow in dtData.Rows
            xlsCells(drSheet("StartRow") + dtData.Rows.IndexOf(drData), drDataCols("DataColumn")) = drData("Col" + drDataCols("DataColumn").toString).toString
        Next
    Next
Next

xlsSheet.SaveAs(newFile)
xlsBook.Close
xls.Quit()

【问题讨论】:

标签: asp.net vb.net excel


【解决方案1】:

每次写入单元格时,Excel 都会重新计算打开的工作簿并刷新屏幕。这两件事都很慢,所以需要设置 Application.Screenupdating=false 和 Application.Calculation=xlCalculationManual

此外,每次写入单元格都会产生很高的开销,因此将数据累积到数组中,然后通过对 Excel 对象模型的一次调用将数组写入范围要快得多。

【讨论】:

  • 我尝试设置Application.Calculation,但是我得到一个运行时错误(认为是HRESULT: 0x800A03EC)。我还尝试设置Application.ScreenUpdating,但这并没有什么不同。我无法写入连续的单元格范围,因为有时模板将自己的列与汇总数据的数据列混合在一起,并且我无法修改模板。
  • +1 表示“更快地将数据累积到数组中,然后将数组写入范围”
  • +1 也是如此。这是一个很好的答案,但 AVD 首先回答了解决我问题的解决方案,所以我接受了他的回答
  • 您应该能够在模板列之间一次写入一列,或者读取混合模板内容和您的内容的列块,根据需要修改数组,然后重写挡回去。访问单个单元格的开销是如此之大,以至于读取然后写入仍然会更快..
【解决方案2】:

使用auto 模式计算,每次数据输入/更改后都会重新计算。我有同样的问题,通过设置Manual计算模式解决。 (参考 MSDN link。)

xls.Calculation = Excel.XlCalculation.xlCalculationManual

此外,此属性只能在打开工作簿后设置,否则会引发运行时错误。

【讨论】:

  • 谢谢。我更新了您的答案以包含代码示例和关于仅在打开工作簿后设置它的说明。
【解决方案3】:

多年来拯救我的一种方法是添加

Application.ScreenUpdating = False

就在我执行一个可能冗长的方法之前,然后

Application.ScreenUpdating = True

直接在代码之后,或者至少在代码的某个稍后点。这会强制 Excel 在完成之前不会在可见屏幕上重绘任何内容。这个问题是我经常发现冗长运行操作的原因。

【讨论】:

  • Excel 无论如何都不会在屏幕上绘制任何内容。该文件在网络服务器上构建,然后保存并返回给用户下载。
猜你喜欢
  • 2021-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多