How to improve the speed of VBA macro code?

前端 未结 7 1519
孤城傲影
孤城傲影 2020-12-06 07:11

I do not have much experience with writing macros, and therefore need the help of this community for the following issue encountered:

My macro copies a range of valu

相关标签:
7条回答
  • 2020-12-06 07:19

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

    I wouldn't do that. Cut,copy & Paste operations are the costliest operations, in terms of processor utilization, in an operating system.

    Instead, you could just assign the value from one cell / range to an another cell / range, as in

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

    Hope you got my point..

    0 讨论(0)
  • 2020-12-06 07:25

    As suggested by a few others in the comments, you should definitely change Application.Calculation to xlCalculationManual and rememeber to set it back to xlcalculationAutomatic at the end. Also try setting Application.Screenupdating = False (and turning that back on again too). Also, bear in mind that .Copy is a very inefficient way to copy cell values - if you really just want the values, loop through the range setting .Value to the .Values in the old range. If you need all the formatting, you're probably stuck with .Copy.

    When you turn off the calc/screen refresh flags, please remember to turn them back on in all circumstances (even when your program exits at a different point, or causes a runtime error). Otherwise all sorts of bad things will happen. :)

    0 讨论(0)
  • 2020-12-06 07:27

    Just reiterating what has already been said:

    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
    

    I'd step through the macro line-by-line to try to locate which line is slow.

    Another alternative - although not sure if it'll speed things up - is to avoid the clipboard and lose the copy/paste so you'd apply a method like the following to move the data:

    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
    
    0 讨论(0)
  • 2020-12-06 07:34

    Please take a look at this article as well. How to speed up calculation and improve performance...

    By all means, Application.calculation= xlCalculationManual is usually the culprit. But we can notice that volatile Excel sheet functions can mostly kill your application on large scale of data processing and functional aspect.

    Also, for your current code following post might not be directly relevant. I find it useful for tips on over-all Excel/VBA performance optimization.

    75 Excel speeding up tips

    PS: I don't have enough reputation to comment on your post. So added as an answer..

    0 讨论(0)
  • 2020-12-06 07:35

    Just a few suggestions (would have posted as a comment but I guess I don't have the rep):

    1. Try refering to cell addresses instead of named ranges (doubt this would be the cause but may be causing some hit to performance)

    2. Do your workbook formulas contain links to other workbooks? Try testing the code on a file with broken links to see if it improves performance.

    3. If neither of these are the issue, my guess is that if the formulas are overly complex, there is probably some processing overhead being added. Try the code on a file containing only values to see if there is any improved performance.

    0 讨论(0)
  • 2020-12-06 07:39

    Best way to improve performance based on my experience is to work on variables in code rather than accessing the spreadsheet every time you want to lookup a value. Save any range you want to work with in a variable(variant) and then iterate through it as if it was the sheet.

        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
    

    From here you can access the data variable as if it was the spreadsheet like - data(row, column) with MUCH MUCH faster read speed.

    0 讨论(0)
提交回复
热议问题