Procedure performing slow, can't identify slowest operation - excel

I have slow performing procedure, it's job is to format sheets in workbook. Mainly to adjust row heights and column widths, apply number formats for columns, plane freezes and to remove autofilters. It is long, and I have deleted several similar parts to shorten it for stackoverflow, but there are no big loops, excel workbook has only several sheets and I have tried to identify slow performing part with timer, but without luck. Any ideas? Is any of these operations extra slow? It takes around 1 minute and 20 seconds to format sheets. It is quite similar to other procedures that are working with data in cells, seems to long, for cosmetic adjustments.
'Procedure to format sheets
Private Sub FormatSheets()
Dim ShHead(1 To 22) As Variant
Dim ShHead2(1 To 19) As Variant
Dim i As Long
Dim Sh As Worksheet
'Creates array of column Headers for sheets "Data", "Process", "Delete"
ShHead(1) = "BizReg_UUK": ShHead(2) = "VDVV_UUK1": ShHead(3) = "VDVV_UUK"
ShHead(4) = "VDVV_NMK": ShHead(5) = "BizReg_Nos": ShHead(6) = "VDVV_Nos"
ShHead(7) = "BizReg_NACE1_2_red": ShHead(8) = "VDVV_NACE_2_red": ShHead(9) = "Nace maiņa"
ShHead(10) = "Nace maiņas avots": ShHead(11) = "BizReg_LKV": ShHead(12) = "VDVV_LKV"
ShHead(13) = "AVG Apgr.": ShHead(14) = "AVG Darb.": ShHead(15) = "VDVV_Adr"
ShHead(16) = "Struktūras": ShHead(17) = "Sākums": ShHead(18) = "Beigas"
ShHead(19) = "Nodarbošanās": ShHead(20) = "NACE": ShHead(21) = "Change it!"
ShHead(22) = "Reason"
'Creates header for sheets "NoResult", "Result"
For i = 1 To 19
If i = 1 Then
ShHead2(i) = ShHead(i)
Else
ShHead2(i) = ShHead(i + 3)
End If
Next
'Loops all sheets in workbook and removes filters, if they exist before data are processed
For Each Sh In ThisWorkbook.Worksheets
If Sh.AutoFilterMode = True Then
Sh.AutoFilterMode = False
End If
Next Sh
'Formating sheet "Result"
With ThisWorkbook.Sheets("Result")
'Clears whole sheet
.UsedRange.Clear
'Text in first row set to bold
.Range("A4:S4").Font.Bold = True
'Creates filter
.Range("A4:S4").AutoFilter
'Writes headers
.Range("A4:S4").Value2 = ShHead2
'Sets width of columns for differnet columns
.Columns("A").ColumnWidth = 10
.Columns("B").ColumnWidth = 25
.Columns("C").ColumnWidth = 30
.Columns("D:E").ColumnWidth = 4
.Columns("F").ColumnWidth = 10
.Columns("G:I").ColumnWidth = 2
.Columns("J").ColumnWidth = 8
.Columns("K").ColumnWidth = 5.5
.Columns("L").ColumnWidth = 35
.Columns("M").ColumnWidth = 3
.Columns("N:O").ColumnWidth = 6
.Columns("P").ColumnWidth = 20
.Columns("Q").ColumnWidth = 20
.Columns("R").ColumnWidth = 5
.Columns("S").ColumnWidth = 40
'Wraps text in column
.Columns("L").WrapText = True
.Columns("S").WrapText = True
'Sets formats for columns containing numbers
.Columns("A").NumberFormat = "#"
.Columns("D:E").NumberFormat = "#"
.Columns("F").NumberFormat = "m/d/yyyy"
.Columns("J").NumberFormat = "### ### ###"
.Range("G:G").HorizontalAlignment = xlCenter
.Range("Q:Q").HorizontalAlignment = xlLeft
'Sets height for all rows
.Rows("1:1048576").RowHeight = 15
End With
'Goes to sheet and cell
Application.Goto ThisWorkbook.Sheets("Result").Range("A5")
'Freezes panes
ActiveWindow.FreezePanes = False
'Freezes panes
ActiveWindow.FreezePanes = True
Application.StatusBar = "Sheet /Result/ formated!"
'Formating sheet "NoResult"
With ThisWorkbook.Sheets("NoResult")
'Clears whole sheet
.UsedRange.Clear
'Text in first row set to bold
.Range("A4:S4").Font.Bold = True
'Creates filter
.Range("A4:S4").AutoFilter
'Writes headers
.Range("A4:S4").Value = ShHead2
'Sets width of columns for differnet columns
.Columns("A").ColumnWidth = 10
.Columns("B").ColumnWidth = 25
.Columns("C").ColumnWidth = 30
.Columns("D:E").ColumnWidth = 4
.Columns("F").ColumnWidth = 10
.Columns("G:I").ColumnWidth = 2
.Columns("J").ColumnWidth = 8
.Columns("K").ColumnWidth = 5.5
.Columns("L").ColumnWidth = 35
.Columns("M").ColumnWidth = 3
.Columns("N:O").ColumnWidth = 6
.Columns("P").ColumnWidth = 20
.Columns("Q").ColumnWidth = 20
.Columns("R").ColumnWidth = 5
.Columns("S").ColumnWidth = 40
'Wraps text in column
.Columns("L").WrapText = True
.Columns("S").WrapText = True
'Sets formats for columns containing numbers
.Columns("A").NumberFormat = "#"
.Columns("D:E").NumberFormat = "#"
.Columns("F").NumberFormat = "m/d/yyyy"
.Columns("J").NumberFormat = "### ### ###"
.Range("G:G").HorizontalAlignment = xlCenter
.Range("Q:Q").HorizontalAlignment = xlLeft
'Sets height for all rows
.Rows("1:1048576").RowHeight = 15
End With
'Goes to sheet and cell
Application.Goto ThisWorkbook.Sheets("NoResult").Range("A5")
'Freezes panes
ActiveWindow.FreezePanes = False
'Freezes panes
ActiveWindow.FreezePanes = True
Application.StatusBar = "Sheet /NoResult/ formated!"
'====================
'Procedure that adds finishing touches at end of procedure
Call EndProcedure
'====================
End Sub

Related

Adjust counter from active sheet to specific sheet

Sub ShowUserForm1()
''Check where the last row is on column B
'' The counter removes one because the first cell is "Topic"
counter = Cells(Rows.Count, 4).End(xlUp).Row
While (Range("D" & counter).Value) = ""
counter = counter - 1
Wend
'' Loop through all the objects in the userform
'' In this example, it's important that the label and textbox will be names Label# / TextBox# because it removes 5 or 7 strings to extract the object number
'' Check the number of the object, and if it's higher than the counter, hides it
For Each formObject In UserForm1.Controls
If TypeName(formObject) = "Label" Then
If Left(formObject.Caption, 5) = "Label" Then
objectNumber = Right(formObject.Name, Len(formObject.Name) - 5)
'Change the label caption according to the cell value
formObject.Caption = Cells(CInt(objectNumber) + 1, 4).Value
If CInt(objectNumber) > counter Then formObject.Visible = False
End If
End If
If TypeName(formObject) = "TextBox" Then
objectNumber = Right(formObject.Name, Len(formObject.Name) - 7)
If objectNumber > 12 Then objectNumber = objectNumber - 12
If CInt(objectNumber) > counter - 1 Then formObject.Visible = False
End If
Next
If counter < 5 Then
'' Change the userform height, you can play with the numbers
UserForm1.Height = 70 + 40 * counter
' Move the button up higher
UserForm1.CommandButton1.Top = 40 + 43 * counter - 60
ElseIf counter < 13 Then
'' Change the userform height, you can play with the numbers
UserForm1.Height = 70 + 35 * counter
' Move the button up higher
UserForm1.CommandButton1.Top = 40 + 35 * counter - 60
ElseIf counter > 13 Then
'' Change the userform height, you can play with the numbers
UserForm1.Height = 70 + 50 * counter
' Move the button up higher
UserForm1.CommandButton1.Top = 40 + 53 * counter - 60
End If
UserForm1.Show
End Sub
My counter starts and only counts with active sheet, is there a way I can convert this to count the same data, but just on a different worksheet?
Active sheet is sheet2, but to have the counter count on sheet1 (inactive)
Thanks in advance!
Edit: Adding full code
Try,
Sub test()
Dim Ws As Worksheet
Dim counter As Long
Set Ws = Sheet1
With Ws
.Visible = xlSheetVisible
counter = .Cells(Rows.Count, 4).End(xlUp).Row
.Visible = xlSheetVeryHidden '<~~ you can't set visibility on the sheet
'.Visible = xlSheetHidden '<~~ you can set visibility on the sheet
End With
Stop
End Sub

How to make a for loop more efficient in VBA?

I am new to VBA and I am looking for something that is similar to python pandas, i.e. avoiding to loop through each rows many times. I am trying to achieve a quite simple task and it takes way too long. What is the best alternative to loops?
Looking around it seems that AutoFilter and Find might do, however I am not sure on what is the best option in my case.
Sub UpdateManualUpdates()
Dim lookUpSheet As Worksheet, updateSheet As Worksheet
Dim valueToSearch As String
Dim i As Long, t As Long
Set lookUpSheet = Worksheets("Manual price changes")
Set updateSheet = Worksheets("Price Build-up")
lastRowLookup = lookUpSheet.Cells(Rows.Count, "F").End(xlUp).Row
lastRowUpdate = updateSheet.Cells(Rows.Count, "B").End(xlUp).Row
'get the number of the last row with data in sheet1 and in sheet2
For i = 6 To lastRowLookup 'i = 2 to last to omit the first row as that row is for headers
valueType = lookUpSheet.Cells(i, 5) 'Type of update - Both, Planning group or GC
valueGroup = lookUpSheet.Cells(i, 3) 'Family group
valueGC = lookUpSheet.Cells(i, 4) 'GC
ValueChange = lookUpSheet.Cells(i, 6) 'What is the % change
'above get the values from the four column into variables
With Worksheets("Price build-up")
For t = 6 To lastRowUpdate
'AW is column 49 target column to update
'M is target column for group, 13
'C is target column for GC, 3
If valueType = "Both" Then
If .Cells(t, 13) = valueGroup And .Cells(t, 3) = valueGC Then
.Cells(t, 49) = ValueChange
End If
End If
If valueType = "Planning group" Then
If .Cells(t, 13) = valueGroup Then
.Cells(t, 49) = ValueChange
End If
End If
If valueType = "GC" Then
If .Cells(t, 3) = valueGC Then
.Cells(t, 49) = ValueChange
End If
End If
Next t
End With
Next i
End Sub
It is slow to access and update the Workbook object. Based on what you have now, a simple way is to convert the worksheet to an array and read the data from the array. Also, set Application.ScreenUpdating = False would make it a little bit faster.
Sub UpdateManualUpdates()
Application.ScreenUpdating = False
Dim lookUpSheet As Worksheet, updateSheet As Worksheet
Dim valueToSearch As String
Dim i As Long, t As Long
Set lookUpSheet = Worksheets("Manual price changes")
Set updateSheet = Worksheets("Price Build-up")
Dim lookUpSheetArray As Variant
Dim updateSheetArray As Variant
lastRowLookup = lookUpSheet.Cells(Rows.Count, "F").End(xlUp).Row
lastRowUpdate = updateSheet.Cells(Rows.Count, "B").End(xlUp).Row
lookUpSheetArray = lookUpSheet.Range("A1:F" & lastRowLookup).Value
updateSheetArray = updateSheet.Range("A1:AW" & lastRowUpdate).Value
For i = 6 To lastRowLookup 'i = 2 to last to omit the first row as that row is for headers
valueType = lookUpSheetArray(i, 5) 'lookUpSheet.Cells(i, 5) 'Type of update - Both, Planning group or GC
valueGroup = lookUpSheetArray(i, 3) 'Family group
valueGC = lookUpSheetArray(i, 4) 'GC
ValueChange = lookUpSheetArray(i, 6) 'What is the % change
'above get the values from the four column into variables
For t = 6 To lastRowUpdate
'AW is column 49 target column to update
'M is target column for group, 13
'C is target column for GC, 3
If valueType = "Both" Then
If updateSheetArray(t, 13) = valueGroup And updateSheetArray(t, 3) = valueGC Then
updateSheet.Cells(t, 49) = ValueChange
End If
End If
If valueType = "Planning group" Then
If updateSheetArray(t, 13) = valueGroup Then
updateSheet.Cells(t, 49) = ValueChange
End If
End If
If valueType = "GC" Then
If updateSheetArray(t, 3) = valueGC Then
updateSheet.Cells(t, 49) = ValueChange
End If
End If
Next t
Next i
Application.ScreenUpdating = True
End Sub
From my experiment, it is about 35% faster. Not a big improvement but just take a minute to update.

Overflow Error with copy-pasting Columns in a Nested-loop

I need to copy columns of y-values from a sheet called "Matrix" and paste them into a sheet called "All Normalized", format is not a concern, but the number of columns isn't just 10 but unlimited.
I need to copy the values from Matrix as a column and divide each cell by the first number in the column to normalize it (first value starts at row 3). And I keep getting this error:"Runtime error '6'- Overflow".
How can I fix this error and properly normalize the data?
Sub NewNorm()
Set WB = ThisWorkbook
Application.ScreenUpdating = False
'X-Values
With WB.Sheets("All Normalized")
[A3].Value = 0
[A4].Value = 1E-18
[A5].Value = 0.0001
[A6].Value = 0.001
[A7].Value = 0.01
[A8].Value = 0.5
[A9].Value = 1
[A10].Value = 2
[A11].Value = 3
[A12].Value = 4
[A13].Value = 5
[A14].Value = 6
[A15].Value = 7
[A16].Value = 8
[A17].Value = 9
[A18].Value = 10
[A19].Value = 20
[A20].Value = 30
[A21].Value = 40
[A22].Value = 50
[A23].Value = 60
[A24].Value = 70
[A25].Value = 80
[A26].Value = 90
[A27].Value = 100
[A28].Value = 150
[A29].Value = 175
[A30].Value = 180
[A31].Value = 185
[A32].Value = 190
[A33].Value = 200
[A34].Value = 300
[A35].Value = 400
[A36].Value = 500
[A37].Value = 1000
End With
Dim ColumnCount As Integer
ColumnCount = 10
Dim Colum As Long
For Columz = 2 To columnz 'Loop through each cell, normalizing
For rowz = 3 To 10
Sheets("All Normalized").Cells(rowz, Columz).Value = Sheets("Matrix").Cells(rowz, Columz).Value / Sheets("Matrix").Cells(3, Columz).Value 'ERROR HERE
Next rowz
Next Columz
Application.ScreenUpdating = True
End Sub
Your narratuive,
...divide each cell by the first number in the column ...
Your data,
[A3].Value = 0
[A4].Value = 1E-18
[A5].Value = 0.0001
[A6].Value = 0.001
...
Your code,
Sheets("All Normalized").Cells(rowz, Columz).Value = _
Sheets("Matrix").Cells(rowz, Columz).Value / Sheets("Matrix").Cells(3, Columz).Value
You cannot divide a number by zero. It results in an overflow error.

Excel HPageBreaks NOT working, using VBSCript

I am trying to set page breaks after row number 40 but its appearing on row number: 45. The output excel file should be of two pages.
Can someone tell me the mistake which i created. Your Help will be greatly appreciated. (I tried to solve this from last 6 hours but no luck :) )
This code is part of my assignment, so i am trying this without changing the column width.
Option Explicit
Dim objExcel,objWorkbook,objSheet,objRange,intPageBreakRow,intRow,i
const xlPageBreakPreview = &H2
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = True
Set objWorkbook = objExcel.Workbooks.Add()
Set objSheet = objWorkbook.Worksheets(1)
Set objRange = objExcel.Range("B:F")
objRange.WrapText = TRUE
objSheet.Range("A:A").ColumnWidth = 1
objSheet.Range("B:B").ColumnWidth = 25
objSheet.Range("C:C").ColumnWidth = 25
objSheet.Range("D:D").ColumnWidth = 45
objSheet.Range("E:E").ColumnWidth = 14
objSheet.Range("F:F").ColumnWidth = 20
objSheet.DisplayAutomaticPageBreaks = False
With objSheet.PageSetup
.Zoom = False
.FitToPagesWide = 1
.FitToPagesTall = 2
End With
objSheet.PageSetup.PrintArea = ""
objSheet.ResetAllPageBreaks
intPageBreakRow=40
intRow=90
objExcel.Cells(1, 1).Value = "Page1 This is the text that we want to wrap in column A."
objExcel.Cells(2, 2).Value = "Page1 This is the text that we want to wrap in column B."
objExcel.Cells(3, 3).Value = "Page1 This is the text that we want to wrap in column C."
objExcel.Cells(5, 5).Value = "Page1 This is the text that we want to wrap in column E."
objExcel.Cells(6, 6).Value = "Page1 This is the text that we want to wrap in column F."
For i=2 To intPageBreakRow
objExcel.Cells(i, 4).Value = "Page1 This is the text that we want to wrap in column D."
Next
objSheet.Range("F72").Value = "Page2 Some text aligned to the center"
objSheet.Range("F72").WrapText = True
For i=intPageBreakRow+1 To intRow
objExcel.Cells(i, 4).Value = "Page2 This is the text that we want to wrap in column D."
Next
'objExcel.Range("A1: F"&intRow).Select
objExcel.ActiveSheet.PageSetup.PrintArea="A1: F"&intRow
objSheet.ResetAllPageBreaks
'objExcel.Rows(intPageBreakRow+1).Select
objSheet.HPageBreaks.Add objSheet.Rows(intPageBreakRow+1)
objExcel.ActiveWindow.View = xlPageBreakPreview
objExcel.Rows.AutoFit

VBA excel , improve performance without loops

I have two identical sheets that i want to take the rows of , that are identical in multiple columns (the sheets are 63 columns always and 504 rows and increasing) , i am using two for loops to increase the row in one and then comparing all the rows in the other with that row then increase the row again and compare all the rows of the other with that row ect. till the last row , then an if loop to see if they match my conditions . The problem is that it is taking too much time (about 8 mins) , i tried to use the lookup functions but it failed because it can only take one value . I added the false screenupdating , calculation , and enableevents and even changed the statusbar to something very basic to improve performance but non of them gave me the result I wanted .
How can i improve performance in any way possible , a new function or anything ??
PS some times some of the conditions are not important and it depends on the true or fasle values on some of the cells .
For Row_S = 2 To MAX_Row_S
SourceMonth = Worksheets(NBG_SourceRegionDataWorksheetName).Cells(Row_S, SOP).Value
SourceMonth = DatePart("m", SourceMonth)
SourceYear = Worksheets(NBG_SourceRegionDataWorksheetName).Cells(Row_S, SOP).Value
SourceYear = DatePart("yyyy", SourceYear)
SourceCarmaker = Worksheets(NBG_SourceRegionDataWorksheetName).Cells(Row_S, carmaker).Value
SourceProject = Worksheets(NBG_SourceRegionDataWorksheetName).Cells(Row_S, Project).Value
SourceFamily = Worksheets(NBG_SourceRegionDataWorksheetName).Cells(Row_S, Family).Value
SourceStatus = Worksheets(NBG_SourceRegionDataWorksheetName).Cells(Row_S, Status).Value
SourceShare = Worksheets(NBG_SourceRegionDataWorksheetName).Cells(Row_S, Share).Value
SourceCst = Worksheets(NBG_SourceRegionDataWorksheetName).Cells(Row_S, "A").Value
SourcePID = Worksheets(NBG_SourceRegionDataWorksheetName).Cells(Row_S, ProjectID).Value
' Take the data from NBG_Data_Region sheet to be Compared with each row of the NBG_Data_Source_Region sheet
For Row_T = 2 To MAX_Row_T
If Row_T >= MAX_Row_T Then
Exit For
End If
NBGMonth = Worksheets(NBG_RegionaDataWorksheetName).Cells(Row_T, SOP).Value
NBGMonth = DatePart("m", NBGMonth)
NBGYear = Worksheets(NBG_RegionaDataWorksheetName).Cells(Row_T, SOP).Value
NBGYear = DatePart("yyyy", NBGYear)
NBGCarmaker = Worksheets(NBG_RegionaDataWorksheetName).Cells(Row_T, carmaker).Value
NBGProject = Worksheets(NBG_RegionaDataWorksheetName).Cells(Row_T, Project).Value
NBGFamily = Worksheets(NBG_RegionaDataWorksheetName).Cells(Row_T, Family).Value
NBGStatus = Worksheets(NBG_RegionaDataWorksheetName).Cells(Row_T, Status).Value
NBGShare = Worksheets(NBG_RegionaDataWorksheetName).Cells(Row_T, Share).Value
NBGCst = Worksheets(NBG_RegionaDataWorksheetName).Cells(Row_T, "A").Value
NBGPID = Worksheets(NBG_RegionaDataWorksheetName).Cells(Row_T, ProjectID).Value
' StatusBar Show
Application.StatusBar = "Running"
'Application.StatusBar = "VerifyMultipleCustomerProjects. Progress: " & Row_S & " of " & MAX_Row_S
' Check if any project in the NBG_Data_Region have multiple customers and add it ti the sheet Issue_MultipleCustomerProjects
' NAF 20161208
'Test with Source of YEAR and MONTH
If ((NBGMonth = SourceMonth Or Worksheets(Issue_MultipleCustomerProjectsWorksheetName).Range("C21") = True) And _
(NBGYear = SourceYear Or Worksheets(Issue_MultipleCustomerProjectsWorksheetName).Range("C25") = True) And _
(SourceCarmaker = NBGCarmaker Or Worksheets(Issue_MultipleCustomerProjectsWorksheetName).Range("G25") = True) And _
(SourceProject = NBGProject Or Worksheets(Issue_MultipleCustomerProjectsWorksheetName).Range("F25") = True) And _
(SourceFamily = NBGFamily Or Worksheets(Issue_MultipleCustomerProjectsWorksheetName).Range("E25") = True) And _
(SourceShare + NBGShare <> 1 Or Worksheets(Issue_MultipleCustomerProjectsWorksheetName).Range("H25") = True) And NBGCst <> SourceCst) Then
Have you tried adding
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
at the beginning of your code, and
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayAlerts = True
at the end of your code?
This will turn off screen updating, events, and alerts causing faster run-time.
Also, loading and unloading arrays are the fastest way if you decide to take that route.
An example of loading an array:
Dim arr() As Variant ' let brackets empty, not Dim arr(1) As Variant !
For Each a In Range.Cells
' change / adjust the size of array
ReDim Preserve arr(1 To UBound(arr) + 1) As Variant
' add value on the end of the array
arr(UBound(arr)) = a.Value
Next
An example of iterating through the array to pull your data:
For Each element In arr 'Each array element
do_something (element)
Next element

Resources