如何提高VBA宏代码的速度?

时间:2012-10-22 17:06:51

标签: excel vba excel-vba

我没有太多编写宏的经验,因此在遇到以下问题时需要此社区的帮助:

我的宏在一个工作表中复制在垂直范围内输入的值范围,然后在另一个工作表中水平粘贴(转置)值。理论上它会将第一张表中的值粘贴到第二张没有内容的工作表的第一行。由于前五行具有内容,因此将值粘贴到第六行。 运行宏时遇到的问题是我觉得它太慢了,所以我希望它运行得更快。

我有相同的宏做同样的事情,但它将值粘贴到另一个工作表到第一行,它运行完美。

因此,我最好的猜测是第二个宏运行缓慢,因为它必须在第六行开始粘贴,前五行可能有一些内容需要花费大量时间才能通过宏(那里)很多单元格引用其他工作簿)以确定下一行的粘贴位置。这是我最好的猜测,因为我几乎不知道宏,我不能肯定问题是什么。

我特此向您提供宏的代码,并真诚地希望有人可以告诉我是什么让我的宏变慢,并为我提供如何让它运行得更快的解决方案。我认为一个解决方案可能是宏可能不应该考虑前五行数据并立即开始在第6行粘贴第一个条目。然后在下一次第7行等等。这可能是一个解决方案,但我不知道如何编写代码以便它会这样做。

感谢您花时间帮我找到解决方案,以下是代码:

Sub Macro1()
Application.ScreenUpdating = False

    Dim historyWks As Worksheet
    Dim inputWks As Worksheet

    Dim nextRow As Long
    Dim oCol As Long

    Dim myCopy As Range
    Dim myTest As Range

    Dim lRsp As Long

    Set inputWks = wksPartsDataEntry
    Set historyWks = Sheet11

      'cells to copy from Input sheet - some contain formulas
      Set myCopy = inputWks.Range("OrderEntry2")

      With historyWks
          nextRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row
      End With

      With inputWks
          Set myTest = myCopy.Offset(0, 2)

          If Application.Count(myTest) > 0 Then
              MsgBox "Please fill in all the cells!"
              Exit Sub
          End If
      End With

      With historyWks
          With .Cells(nextRow, "A")
              .Value = Now
              .NumberFormat = "mm/dd/yyyy hh:mm:ss"
          End With
          .Cells(nextRow, "B").Value = Application.UserName
          oCol = 3
          myCopy.Copy
          .Cells(nextRow, 3).PasteSpecial Paste:=xlPasteValues, Transpose:=True
          Application.CutCopyMode = False
      End With

      'clear input cells that contain constants
      With inputWks
        On Error Resume Next
           With myCopy.Cells.SpecialCells(xlCellTypeConstants)
                .ClearContents
                Application.GoTo .Cells(1) ', Scroll:=True
           End With
        On Error GoTo 0
      End With

Application.ScreenUpdating = True
End Sub

7 个答案:

答案 0 :(得分:7)

重申已经说过的话:

Option Explicit

Sub Macro1()

'turn off as much background processes as possible
With Excel.Application
        .ScreenUpdating = False
        .Calculation = Excel.xlCalculationManual
        .EnableEvents = False
End With

    Dim historyWks As Excel.Worksheet
    Dim inputWks As Excel.Worksheet

    Dim nextRow As Long
    Dim oCol As Long

    Dim myCopy As Excel.Range
    Dim myTest As Excel.Range

    Dim lRsp As Long

    Set inputWks = wksPartsDataEntry
    Set historyWks = Sheet11

      'cells to copy from Input sheet - some contain formulas
      Set myCopy = inputWks.Range("OrderEntry2")

      With historyWks
          nextRow = .Cells(.Rows.Count, 1).End(Excel.xlUp).Offset(1, 0).Row
      End With

      With inputWks
          Set myTest = myCopy.Offset(0, 2)

          If Excel.Application.Count(myTest) > 0 Then
              MsgBox "Please fill in all the cells!"
              GoTo QuickExit
          End If
      End With

      With historyWks
          With .Cells(nextRow, 1)
              .Value = Now
              .NumberFormat = "mm/dd/yyyy hh:mm:ss"
          End With
          .Cells(nextRow, 2).Value = Excel.Application.UserName
          oCol = 3
          myCopy.Copy
          .Cells(nextRow, 3).PasteSpecial Paste:=Excel.xlPasteValues, Transpose:=True
          Excel.Application.CutCopyMode = False
      End With

      'clear input cells that contain constants
      With inputWks
        On Error Resume Next
           With myCopy.Cells.SpecialCells(Excel.xlCellTypeConstants)
                .ClearContents
                Excel.Application.Goto .Cells(1) ', Scroll:=True
           End With
        On Error GoTo 0
      End With

    Calculate

QuickExit

With Excel.Application
        .ScreenUpdating = True
        .Calculation = Excel.xlAutomatic
        .EnableEvents = True
End With

End Sub

我逐行浏览宏来尝试找出哪条线很慢。

另一种选择 - 虽然不确定它是否能加快速度 - 但是要避免使用剪贴板并丢失copy/paste,以便您应用以下方法来移动数据:

Option Explicit

Sub WithoutPastespecial()

'WORKING EXAMPLE

Dim firstRange As Range
Dim secondRange As Range

Set firstRange = ThisWorkbook.Worksheets("Cut Sheet").Range("S4:S2000")
With ThisWorkbook.Worksheets("Cutsheets")
    Set secondRange = .Range("A" & .Rows.Count).End(Excel.xlUp).Offset(1)
End With

With firstRange
      Set secondRange = secondRange.Resize(.Rows.Count, .Columns.Count)
End With
secondRange.Value = firstRange.Value

End Sub

答案 1 :(得分:7)

根据我的经验提高性能的最佳方法是在代码中处理变量,而不是每次要查找值时都访问电子表格。 将您想要使用的任何范围保存在变量(变量)中,然后像在工作表中一样迭代它。

    dim maxRows as double
    dim maxCols as integer.
    dim data as variant
with someSheet
    maxRows = .Cells(rows.count, 1).end(xlUp).row             'Max rows in sheet
    maxCols = .Cells(1, columns.count).end(xlToLeft).column   'max columns in sheet
    data = .Range(.Cells(1,1), .Cells(maxRows, maxCols))      'copy range in a variable
end with

从这里你可以访问数据变量,就好像它是电子表格一样 - 数据(行,列),读取速度更快。

答案 2 :(得分:5)

请看一下这篇文章。 How to speed up calculation and improve performance...

无论如何,Application.calculation = xlCalculationManual通常是罪魁祸首。但是我们可以注意到,易变的Excel工作表函数可能会在大规模的数据处理和功能方面大部分时间内终止您

另外,对于您当前的帖子,后面的帖子可能并不直接相关。我发现它对于整体Excel / VBA性能优化的提示非常有用。

75 Excel speeding up tips

PS:我没有足够的声誉来评论你的帖子。所以添加了答案..

答案 3 :(得分:1)

正如评论中的其他几个人所建议的那样,你绝对应该将Application.Calculation更改为xlCalculationManual并将其重新设置为最后将其设置回xlcalculationAutomatic。同时尝试设置Application.Screenupdating = False(并再次将其重新打开)。另外,请记住.Copy是一种非常低效的复制单元格值的方法 - 如果你真的只想要值,则循环遍历范围设置。值为旧范围内的.Values。如果你需要所有的格式,你可能会坚持使用.Copy。

当您关闭计算/屏幕刷新标记时,请记住在所有情况下重新打开它们(即使您的程序在不同的点退出,或导致运行时错误)。否则会发生各种不好的事情。 :)

答案 4 :(得分:1)

只是一些建议(会发表评论,但我想我没有代表):

  1. 尝试引用单元格地址而不是命名范围(怀疑这可能是原因,但可能会导致性能下降)

  2. 您的工作簿公式是否包含指向其他工作簿的链接?尝试在包含损坏链接的文件上测试代码,看看它是否可以提高性能。

  3. 如果这些都不是问题,我的猜测是,如果公式过于复杂,可能会增加一些处理开销。尝试仅包含值的文件上的代码,以查看是否有任何改进的性能。

答案 5 :(得分:0)

您可以通过在更改单元格值期间停止计算来提高速度,之后您可以启用它。请点击链接。 http://webtech-training.blogspot.in/2013/10/how-to-stop-heavy-formula-calculation.html

答案 6 :(得分:0)

.Cells(nextRow,3).PasteSpecial Paste:= xlPasteValues,Transpose:= True           Application.CutCopyMode = False

我不会这样做。剪切,复制和在处理器利用率方面,粘贴操作是操作系统中最昂贵的操作。

相反,您可以将值从一个单元格/范围分配给另一个单元格/范围,如

    Cells(1,1) = Cells(1,2)  or Range("A1") = Range("B1")

希望你明白我的意思..