I have an excel file with a VBA code (Not written by me)
How this code works is user enters a 6 digit number in a user form, the VBA then checks another sheet and if this 6 digit number is present on the worksheet.
If it does, it changes the stage, but if it doesn't it adds this 6 digit number to the worksheet
It used to work perfectly, but now because the excel file has grown in the number of rows, almost 6000 rows, this code is become very slow, takes up to 20 seconds to update the sheet
Can someone please help me speed this code up, or suggest another way to acheive it
The code is below
Private Sub cmdPSDUdate_Click()
Dim x
If (Me.PSDUDateRow = "") + (Me.PSDStageCB.ListIndex = -1) Then Exit Sub
With Sheets("psdata stage cals").ListObjects("PSDataStageCals")
x = Application.Match(Val(Me.PSDUDateRow), .ListColumns(1).DataBodyRange, 0)
If IsNumeric(x) Then
.ListRows(x).Range(2) = Me.PSDStageCB.Value
Else
.ListRows.Add.Range = Array(Val(Me.PSDUDateRow), Me.PSDStageCB)
End If
End With
Me.PSDUDateRow.Value = ""
Me.PSDStageCB.Value = ""
Me.PSDUDateRow.SetFocus
End Sub
Thanks in advance
Rahul
You could turn off screenupdating, automatic calculations etc
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
‘Place your macro code here
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True
In general, there are two ways to speed up VBA code:
Write good code, that does not use Select, Activate, ActiveCell, Selection etc - How to avoid using Select in Excel VBA
Refer to these routines on the start and on the end of the code:
Public Sub OnEnd()
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.AskToUpdateLinks = True
Application.DisplayAlerts = True
Application.Calculation = xlAutomatic
ThisWorkbook.Date1904 = False
Application.StatusBar = False
End Sub
Public Sub OnStart()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.AskToUpdateLinks = False
Application.DisplayAlerts = False
Application.Calculation = xlAutomatic
ThisWorkbook.Date1904 = False
ActiveWindow.View = xlNormalView
End Sub
(For improvement ideas, kindly make PullRequest)
I think that Calculation should be always set to xlAutomatic, as far as if you need xlCalculationManual to speed up, it is a good idea to refactor the code. Furthermore manual calculation is too risky.
The same goes for Date1904 - it is always set to False.
In addition to the tweaks suggested by Storax, your code is slow because you are bringing data cell-by-cell over the Excel/VBA divide.
Furthermore, you can radically speed up your MATCH function by using the Binary version of it. Have a read of http://dailydoseofexcel.com/archives/2015/04/23/how-much-faster-is-the-double-vlookup-trick/ and also try to minimise the amount of individual transfers you do across the Excel/VBA divide by either performing the lookups entirely within the Excel sheet (by using VBA to write the formula in the sheet and execute it there) or by bringing all the data into VBA in one go using variant arrays, performing your logic, and then by dumping it back in one go. Google "Efficient way to transfer data between Excel and VBA" or something similar. Also check out any articles from Charles Williams on the subject.
I don't see anything wrong with your code. Perhaps the Workbook itself is the culprit. Is it becoming huge and slow to open ?
If yes, try searching for 'cleanup excel file'.
Some results I found:
https://excelfilecleaner.codeplex.com/
https://support.microsoft.com/en-us/help/3070372/how-to-clean-up-an-excel-workbook-so-that-it-uses-less-memory
When crunching large chunks of data in Excel that requires frequent referencing of cells, it’s always much much faster to copy the data to an array (copy the entire worksheet if necessary), process the data within the array, and then write back to the worksheet if necessary. Copying data from worksheet to array is a one line command that is very very fast. Same with array to worksheet. Relatively speaking, referencing cells is a very time consuming process compared with referencing elements of an array.
Related
I am trying to run this simple VBA code when opening excel workbook.
Private Sub Workbook_Open()
Application.CommandBars.ExecuteMso ("HideRibbon")
ActiveWindow.DisplayHorizontalScrollBar = False
ActiveWindow.DisplayHeadings = False
ActiveWindow.DisplayGridlines = False
Application.DisplayFormulaBar = False
End Sub
However, this works 50% of times and I'm still wondering why. In one of the sheet there is conditional formatting that I have to keep for reporting purposes). I have tried many solutions suggested in this community but they didn't work at all.
Can somebody help me to figure it out?
Many thanks
I tried to look for answers but am not finding anything that has worked so far. I have some code that works for some people and doesn't work for others (using same version of Excel) when running this code:
Private Sub Workbook_Open()
Application.ScreenUpdating = False
Application.ExecuteExcel4Macro "SHOW.TOOLBAR(""Ribbon"",False)"
Application.DisplayFormulaBar = False
Sheets("Discount").Activate
ActiveSheet.Unprotect Password:="01"
ActiveSheet.Range("G14:O15,O18:O19,D29:I29,D31:I31,D33:I33,D35:I35,D37:I37").ClearContents
ActiveSheet.Shapes("Option Button 31").ControlFormat.Value = xlOn
OptionButton31_Click
Application.ScreenUpdating = True
End Sub
The error shows up at Sheets.("Discount").Activate
the spelling of the worksheet is correct. I also tried
Private Sub Workbook_Open()
ActiveWorkbook.Unprotect Password:="01"
Application.ScreenUpdating = False
Application.ExecuteExcel4Macro "SHOW.TOOLBAR(""Ribbon"",False)"
Application.DisplayFormulaBar = False
ThisWorkbook.Sheets("Discount").Activate
ActiveSheet.Unprotect Password:="01"
ActiveSheet.Range("G14:O15,O18:O19,D29:I29,D31:I31,D33:I33,D35:I35,D37:I37").ClearContents
ActiveSheet.Shapes("Option Button 31").ControlFormat.Value = xlOn
OptionButton31_Click
ActiveWorkbook.Protect Password:="01"
Application.ScreenUpdating = True
And still getting the error. I am having a hard time figuring it out because it works for me every time, but doesn't for other people.
Solution 1:
Instead of Sheets.("Discount").Activate write Sheets("Discount").Activate and it should work. E.g., remove the dot.
Solution 2:
If this does not work, try to make sure that this sheet is visible. E.g. write before the line with the error the following:
Sheets("Discount").Visible = True
In general, in VBA try to avoid ActiveSheet, ActiveWorkbook, ActiveCell -
How to avoid using Select in Excel VBA
As noted by #Mat's Mug, consider using Worksheets("Discount").Visible, when you refer to Worksheets, because the Sheets collection contains Charts as well.
Try using:
Sheets("Discount").Visible
Sheets("Discount").Select
If this doesn't work, let me know and I'll see if there's anything else I can recommend. If you make a note of any error messages, this may help. Also, try running it with screenupdating not turned off as the person above suggested - then you will see if there's a specific action that's making it fall over.
I am using VBA to try and call a series of sub routines from the main sub routine. When I combine all of the subroutines with code similar to the following, I get #N/A for the cells that the formula is suppose to be ignoring.
Sub Main()
'Turn off autocalculation
Application.Calculation = xlCalculationManual
Application.DisplayStatusBar = False
'*********************************************************
'A bunch of other code
'*********************************************************
Call Sub_Routine1
Call Sub_Routine2
Call Sub_Routine3
Call Sub_Routine4
Call Sub_Routine5
Call Sub_Routine6
Call Sub_Routine7
'This is the sub routine that is not working correctly
Call Material_Formulas
'Turn back on the autocalculation function
Application.Calculation = xlAutomatic
'*********************************************************
'A bunch of other code
'*********************************************************
Application.DisplayStatusBar = True
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
When I remove the Material_Formulas subroutine from the Main sub routine and run it separately using the following script, it executes as it is suppose to and looks like the image bellow.
Private Sub Material_Formulas()
'Turn on manual calculation
Application.Calculation = xlCalculationManual
Dim lRow As Integer
Dim tiesMaterial As String
Dim result As String
lRow = Sheets("Material").Range("A2").End(xlDown).Row
lCol = Sheets("Material").Range("A2").End(xlToRight).Column
'Starts the count at column CU
endCount = lCol - 1
For c = 99 To endCount
For r = 3 To lRow
tiesMaterial = Cells(r, 87).Value
'Looks to see if the cells starting at CU2 contains a number and then iterates through each cell in row 3 to add a formula
If tiesMaterial = "TIES MATERIAL" Then
'Defines the unique ID and calendar year cells for the index-match-match function
materialID = Sheets("Material").Cells(r, "CQ").Address(False, False)
materialYear = Sheets("Material").Cells(2, c).Address(False, False)
'Starting in cell CU3 it adds the formula =INDEX(BOM_Summary_Array,MATCH(CQ3,BOM_Summary_ID,0),MATCH(CU2,BOM_Summary_Head,0))
Sheets("Material").Cells(r, c).Formula = "=INDEX(BOM_Summary_Array,MATCH(Material!" & materialID & ",BOM_Summary_ID,0),MATCH(Material!" & materialYear & ",BOM_Summary_Head,0))"
End If
Next r
Next c
'Turn on the auto calculation function
Application.Calculation = xlAutomatic
End Sub
What am I doing wrong? How does it run fine when I manually and independently select it but it fails when I combine it with the other sub routines?
Before anything else you need to improve your code. I can pretty much guarantee that this is likely happening because of poorly written code. For example:
materialID = Sheets("Material").Cells(r, "CQ").Address(False, False)
materialYear = Sheets("Material").Cells(2, c).Address(False, False)
Note that materialID and materialYear are never declared. This means they are of the type Variant (you need to add Option Explicit to the top of your code modules for this exact reason). The funny thing about Variants is they, you guessed it, vary. MaterialID could be a string, an int, a long, a decimal, a date, an array, a range, etc. We can assume that the address of the range is going into materialID, but we can't be certain.
Additionally, notice the pesky Sheets("Material"). What this actually is saying is ActiveWorkboook.Sheets("Material")`. Qualify your references otherwise you have pretty much no clue what is actually happening.
What can easily be happening in the case of your code is that the address is correctly going into materialID as a string, but it is the address from another workbook with a sheet named "Material". Unlikely, but possible.
What we do know is quite likely is that the ActiveWorkbook is changing in some capacity, likely in Sub_Routine7 (sidenote here, you must descriptively name subroutines otherwise your code is nowhere near maintainability).
Good luck with this, but I highly suggest not bothering to try to debug the situation until you have qualified your ranges, declared all your variables, and added Option Explicit.
I have the following code:
Application.ScreenUpdating = false
Set rng = Application.Selection.Range
For i = 0 To 25000
ActiveDocument.Fields.Add rng, WdFieldType.wdFieldExpression, "1+2", False
Next i
Application.ScreenUpdating = true
Is there a way to further increase its performance?
(Noticed that if I would hide the application Application.Visible = false its faster but I don't really want do that)
(The amount is an edge case, but I would still like to make it plausible and currently, it's taking around 30 minutes to complete, which no user will wait)
I have an excel vba macro that creates and formats a sales quote. I have a function that autofits merged cells. I use this function to autofit description fields as some of them are long and some are short. On a typical quote, this function is called around 40 times. The macro completes in under a second. If I run the exact same macro again (perhaps with different settings on how it will display) it takes upwards of 30-60 seconds. There's nothing in the rest of the macro that slows down with each run except for the following block:
Is there something that could be making this code run slower for the exact same set of inputs?
Sub AutoFit_Height(ByVal Target As Range)
Dim MergeWidth As Single
Dim cM As Range
Dim CWidth As Double
Dim NewRowHt As Double
With Target
.MergeCells = False
CWidth = .Cells(1).ColumnWidth
MergeWidth = 0
For Each cM In Target
cM.WrapText = True
MergeWidth = cM.ColumnWidth + MergeWidth
Next
'small adjustment to temporary width
MergeWidth = MergeWidth + Target.Cells.count * 0.66
.Cells(1).ColumnWidth = MergeWidth
.EntireRow.AutoFit
NewRowHt = .RowHeight
.Cells(1).ColumnWidth = CWidth
.MergeCells = True
.RowHeight = NewRowHt
End With
End Sub
Go to the start of the main body of code (the one that calls this subroutine) and add
Application.ScreenUpdating = False
as one of the first lines. Then at the end of that function, after this subroutine has been called, turn it back on:
Application.ScreenUpdating = True
This will keep it from redrawing the screen for every change in column/row size, resulting in a significantly faster run time.
Optimazing your script might start with
using xlsb file saving extension
look for any unecessary formatting by deleting unused rows and
columns and saving so to make the vertical and horizontal scroll bar
scroll just to the necessary data
Use named range as much as possible
This could be added to your script
At the start of the main module from where you call other modules or functions
With Application
.DisplayAlerts = False
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
At the end of the main module task is complete
With Application
.DisplayAlerts = True
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
As you are manipulating many worksheet objects that's quite a cpu intensive process. A change in algorithm could improve time needed for this process. Maybe you should post the entire code so to help you further.
Cheers,
Pascal
http://multiskillz.tekcities.com
Autofit is painfully slow for me to. The only thing I can add to any other answer here is to put it in a separate module and only call it when necessary or time is available. Also, to implement last, once all other code is tested and working.