通过Excel VBA中的类进行迭代

时间:2009-11-17 15:22:04

标签: performance excel vba

我为一个名为Terminal的实体创建了一个类模块。我有一个方法,通过浏览175个单独的工作表并从特定单元格中提取正确的数据来填充此类。这个过程非常快(大约2秒),但是当我尝试将这些数据写回到新的工作表时,它需要更长的时间(45秒)。看起来这个过程应该至少和填充类一样快,因为它永远不会离开工作表,但事实并非如此。下面是我用来将数据写入工作表的过程,我是否忽略了导致其运行速度这么慢的事情?

Application.ScreenUpdating = False
 Dim rowNumber As Integer
 Dim colNumber As Integer
 Dim terminalCode As String
 Dim terminal As clsTerminal

 rowNumber = 7
 colNumber = 1

 For Each terminal In terminals

        'Add hyperlink to each terminal code
        Sheets("Terminal Summary").Hyperlinks.Add Anchor:=Cells(rowNumber, colNumber), Address:="", _
            SubAddress:=terminal.terminalCode + "!A1", TextToDisplay:=terminal.terminalCode

        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 1).Value = "Current"

        'Current period
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 2).Value = terminal.iBShipments
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 3).Value = terminal.oBShipments
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 4).Value = terminal.iBNetRevenue
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 5).Value = terminal.oBNetRevenue
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 6).Value = terminal.iBWeight
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 7).Value = terminal.oBWeight
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 8).Value = terminal.iBMileage
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 9).Value = terminal.oBMileage
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 10).FormulaR1C1 = "=IFERROR(RC[-4]/RC[-8],0)"
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 11).FormulaR1C1 = "=IFERROR(RC[-4]/RC[-8],0)"
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 12).FormulaR1C1 = "=IFERROR(RC[-8]/RC[-10],0)"
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 13).FormulaR1C1 = "=IFERROR(RC[-8]/RC[-10],0)"
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 14).FormulaR1C1 = "=IFERROR(RC[-10]/(RC[-8] / 100),0)"
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 15).FormulaR1C1 = "=IFERROR(RC[-10]/(RC[-8] / 100),0)"
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 16).FormulaR1C1 = "=IFERROR(RC[-12]/RC[-8],0)"
        Sheets("Terminal Summary").Cells(rowNumber, colNumber + 17).FormulaR1C1 = "=IFERROR(RC[-12]/RC[-8],0)"

        rowNumber = rowNumber + 1
    Next terminal

修改 我应该注意终端是终端类的集合

3 个答案:

答案 0 :(得分:4)

此类代码的常见缺陷是,每次将数据写入电子表格时,Excel都会运行计算(它会评估工作簿中的所有公式,以查看是否需要使用新数据计算)

如果您在循环之前禁用自动计算,然后再重新启用它,事情会更快地移动:

Application.Calculation = xlCalculationManual
For Each terminal In terminals
...
Next terminal
Application.Calculation = xlCalculationAutomatic

答案 1 :(得分:3)

你已经有了很大的节省(在写作时关闭了自动计算)但是还有一些其他的小技巧要记住未来。

首先,每次从VBA写入单元格时,由于VBA进入工作簿/ wootksheet / cell地址并执行写入而导致开销。在一次调用中写入多个单元只会产生一次开销。因此,将多个值打包到一个数组中,并将该数组写入多个单元格会增加时间。几行不值得,但值得为数百人努力。

此外,每个“点”还有一点点开销。 “虚线”表达式中的术语(例如Sheets("Terminal Summary").Cells(rowNumber, colNumber + 2))需要Excel / VBA来确定每个调用中涉及哪些对象。在某些情况下(特别是在引用远程对象时),开销可能很大。 VB为我们提供了With...End With构造,以减少继续解析这些引用的需要:以点开头的每个表达式都会自动引用下一个最外层With中的对象。

所以我们可能得到这样的东西:

With Sheets("Terminal Summary")
    .Cells(rowNumber, colNumber + 2).Resize(1, 8) = terminal.InfoArray
    .Cells(rowNumber, colNumber + 10).FormulaR1C1 = "=IFERROR(RC[-4]/RC[-8],0)"
    .Cells(rowNumber, colNumber + 11).FormulaR1C1 = "=IFERROR(RC[-4]/RC[-8],0)"
    .Cells(rowNumber, colNumber + 12).FormulaR1C1 = "=IFERROR(RC[-8]/RC[-10],0)"
    .Cells(rowNumber, colNumber + 13).FormulaR1C1 = "=IFERROR(RC[-8]/RC[-10],0)"
    .Cells(rowNumber, colNumber + 14).FormulaR1C1 = "=IFERROR(RC[-10]/(RC[-8] / 100),0)"
    .Cells(rowNumber, colNumber + 15).FormulaR1C1 = "=IFERROR(RC[-10]/(RC[-8] / 100),0)"
    .Cells(rowNumber, colNumber + 16).FormulaR1C1 = "=IFERROR(RC[-12]/RC[-8],0)"
    .Cells(rowNumber, colNumber + 17).FormulaR1C1 = "=IFERROR(RC[-12]/RC[-8],0)"
End With

我把数组的东西塞进Terminal类,就像这样:

Public Property Get InfoArray() As Variant
    InfoArray = Array(iBShipments, oBShipments, iBNetRevenue, oBNetRevenue, iBWeight, oBWeight, iBMileage, oBMileage)
End Property

在终端信息全部完成后,每列写一次,可以更有效地输出公式。

要考虑的一些事情,至少......

答案 2 :(得分:1)

你已经有90%的答案,但这里还有一个性能提示。而不是做:

Sheets("Terminal Summary").Cells(rowNumber, colNumber + 10).FormulaR1C1 = "=IFERROR(RC[-4]/RC[-8],0)"

您可以使用以下事实:您将相同的公式分配给列中的所有单元格,并在一个语句中执行:

Sheets("Terminal Summary").Cells(7, 11).Resize(terminals.Count, 1).FormulaR1C1 = "=IFERROR(RC[-4]/RC[-8],0)"
相关问题