I have few models like this below for diferent tools i manage. I am trying to clean/improve the code... Could you help me?
All the informations comes inside columns B to N, and the column P concatenate it with ";". There are 6 sheets i do the same thing.
Any ideas to have a better performance and cleaner code?
Private Sub bov_mobile()Sheets("Mobile").SelectColumns("p").Clearfim = Cells(Rows.count, 1).End(xlUp).RowFor i = 2 To fimRange("A" & i).Select If ActiveCell.Offset(0, 14).Value = "BOV" Or ActiveCell.Offset(0, 14).Value = "BOV BMF" Then Range("P" & i).FormulaR1C1 = "=RC[-14]&"";""&RC[-13]&"";""&RC[-12]&"";""&RC[-11]&"";""&RC[-10]&"";""&RC[-9]&"";""&RC[-8]&"";""&RC[-7]&"";""&RC[-6]&"";""&RC[-5]&"";""&RC[-4]&"";""&RC[-3]&"";""&RC[-2]&"";""" Range("P" & i).Copy Selection.PasteSpecial Paste:=xlPasteValues Application.CutCopyMode = False End IfNext i ' ############### ORDER BY Range("p2", Cells(Rows.count, 16).End(xlUp)).Select Selection.Sort Key1:=Range("P2"), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormalRange("P2", Cells(Rows.count, 16).End(xlUp)).CopySheets("Mod_Bov").SelectCells(Rows.count, 1).End(xlUp).Offset(1, 0).PasteSpecial xlPasteValuesEnd Sub- 1\$\begingroup\$Yeah, better for CodeReview - but may I suggest firstavoid using
.Select\$\endgroup\$BruceWayne– BruceWayne2016-07-13 14:24:48 +00:00CommentedJul 13, 2016 at 14:24 - 6\$\begingroup\$It's a little hard to tell what your code does without actually analyzing it - pleaseedit your title to tell us what the code is doing (all questions on this site want to achieve simpler/better code) - thanks, and welcome to CR!\$\endgroup\$Mathieu Guindon– Mathieu Guindon2016-07-13 14:42:09 +00:00CommentedJul 13, 2016 at 14:42
4 Answers4
The quickest fix you can make to improve speed is removing all of theSelect statements, and instead just referencing those selected ranges with variables. (Select slows down any VBA code significantly). You can also toggleApplication.ScreenUpdating to False before the code and True after to gain run-time speed.
- \$\begingroup\$The screenUpdating is = false inside the sub that call this one. The main sub calls bov_mobile, bov_xxx, bov_xxx2 and etc... I will try to avoid the select... Im doing first steps.. :)\$\endgroup\$Diogo– Diogo2016-07-13 14:44:20 +00:00CommentedJul 13, 2016 at 14:44
The main problem here is the use of .Select. There's no need to select the cells in order to manipulate them. It forces the GUI to update which is a very slow operation in Excel and it also causes Selection Change events and similar code to fire in the background. Simply perform the action on the cells directly. So, for example this:
Sheets("Mobile").SelectColumns("p").ClearShould be changed to this
Sheets("Mobile").Columns("p").ClearThe same with your loop. Avoid this
ActiveCell.Offset(0, 14).ValueIn favour of this
Cells(i,22).ValueColumn 22 is 14 columns from column I. This increases efficiency in two ways: by not selecting the cell and also by avoiding the unnecessary calculation carried out by the Offset function.
- \$\begingroup\$@absinthe Ty! I got your point, but i call this sub from other sheet, so i have to move to it and start its routines.\$\endgroup\$Diogo– Diogo2016-07-13 14:42:58 +00:00CommentedJul 13, 2016 at 14:42
- 1\$\begingroup\$When you use
Sheets("Mobile")the VBA code will reference the sheet "Mobile"no matter what sheet is currently visible in the user interface without having to.Activateor.Selectit. That's the beauty of explicitly referencingWorkbooks()orWorksheets()(orSheets()).\$\endgroup\$FreeMan– FreeMan2016-07-13 15:10:08 +00:00CommentedJul 13, 2016 at 15:10
First, things that jump out at me:
Private Sub bov_mobile()
Don't use_ in Sub/function names. In VBA,_ in a method name denotes an event-triggered Method E.G.Workbook_Open orButton_OnClick so avoid it in your own method names.
Option Explicit
That should be at the top of every VBA module you ever write. It requires you to declare variable names before you use them. E.G.Dim i As Long. This makes sure that youcan't do something like this:
Dim fim As Longfim = Cells(Rows.Count, 1).End(xlUp).RowFor i = 2 to fin ' <-- TypoBecause the compiler will ask whyfin hasn't been declared as a variable. Simple typos like that are a real pain, so don't give them a chance to appear in your code in the first place.
Use the Object Model
VBA has objects for everything.Workbooks,Worksheets,Ranges etc. Rather than something like this:
Sheets("Mobile").SelectSheets("Mobile").Range("A" & i).SelectSheets("Mobile").Range("P" & i).Copy Sheets("Mobile").Range("A" & i).PasteSpecial Paste:= xlPasteValuesYou should instead:
Dim mobileSheet As WorksheetSet mobilesheet = Sheets("Mobile")mobileSheet.SelectmobileSheet.Range("A" & i).SelectmobileSheet.Range("P" & i).Copy mobileSheet.Range("A" & i).PasteSpecial Paste:= xlPasteValuesAnd then:
Dim mobileSheet As WorksheetSet mobilesheet = Sheets("Mobile")With mobileSheet .Select .Range("A" & i).Select .Range("P" & i).Copy .Range("A" & i).PasteSpecial Paste:= xlPasteValuesEnd WithAnd then:
Dim mobileSheet As WorksheetSet mobileSheet = Sheets("Mobile")With mobileSheet Dim pasteCell As Range Set pasteCell = .Range("A" & i) Dim copyCell As Range Set copyCell = .Range("P" & i)End WithWith CopyCell .FormulaR1C1 = .... .CopyEnd WithpasteCell.PasteSpecial Paste:=xlPasteValuesNotice how there are noSelects. There are noActives. There are noOffsets. Everything is descriptively named.
Your sub using proper objects and better naming:
Private Sub bov_mobile() Dim mobileSheet As Worksheet Set mobileSheet = ThisWorkbook.Sheets("Mobile") Dim bovSheet As Worksheet Set bovSheet = ThisWorkbook.Sheets("Mod_Bov") mobileSheet.Columns("P").Clear Dim finalRow As Long With mobileSheet finalRow = .Cells(.Rows.Count, 1).End(xlUp).Row End With Dim baseCell As Range Dim copyCell As Range Dim columnOffset As Long Dim currentRow As Long For currentRow = 2 To finalRow With mobileSheet Set baseCell = .Cells(currentRow, 1) Set copyCell = .Cells(currentRow, 15) End With With copyCell If .Text = "BOV" Or .Text = "BOV VMF" Then .FormulaR1C1 = "=RC[-14]&"";""&RC[-13]&"";""&RC[-12]&"";""&RC[-11]&"";""&RC[-10]&"";""&RC[-9]&"";""&RC[-8]&"";""&RC[-7]&"";""&RC[-6]&"";""&RC[-5]&"";""&RC[-4]&"";""&RC[-3]&"";""&RC[-2]&"";""" .Copy baseCell.PasteSpecial xlPasteValues Application.CutCopyMode = False End If End With Next currentRow Dim sortRange As Range With mobileSheet Set sortRange = .Range(.Cells(2, 16), .Cells(finalRow, 16)) sortRange.Sort Key1:=.Cells(2, 16), Order1:=xlAscending, Header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal End With sortRange.Copy Dim bovFinalRow As Long With bovSheet bovFinalRow = .Cells(.Rows.Count, 1).End(xlUp).Row End With bovSheet.Cells(bovFinalRow + 1, 1).PasteSpecial xlPasteValuesEnd SubMuch, much cleaner and easier to understand.
- 1\$\begingroup\$SettingRequire Variable Declaration within the VBE's Tools ► Options ► Editor property page will put theOption Explicit statement at the top of each newly created code sheet.\$\endgroup\$user66882– user668822016-07-14 18:50:35 +00:00CommentedJul 14, 2016 at 18:50
I'd add my 0.02 cents
Pasting Values
it's always much faster to use
Range1.Value = Range2.ValueprovidedRange1 andRange2 have the same size
Avoiding formulas
they mean writing into cells which is a time consuming activity, and possibly raise sheet calculation
the following code:
"=RC[-14]&"";""&RC[-13]&"";""&RC[-12]&"";""&RC[-11]&"";""&RC[-10]&"";""&RC[-9]&"";""&RC[-8]&"";""&RC[-7]&"";""&RC[-6]&"";""&RC[-5]&"";""&RC[-4]&"";""&RC[-3]&"";""&RC[-2]&"";"""can be substituted with:
Join(.Offset(, -13).Resize(, 14), ";")AvoidWith inside a loop
you have such code
For currentRow = 2 To finalRow With mobileSheet ...Next currentRowwhich means that a reference tomobileSheet is made at every loop
so just take it outside the loop:
With mobileSheet For currentRow = 2 To finalRow .... Next currentRowEnd Withadjusting what necessary
UseWith
to lessen memory charge in referencing the same object multiple time
so that:
With mobileSheet For currentRow = 2 To finalRow .... Next currentRowEnd WithDim sortRange As RangeWith mobileSheet Set sortRange = .Range(.Cells(2, 16), .Cells(finalRow, 16)) sortRange.Sort Key1:=.Cells(2, 16), Order1:=xlAscending, header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormalEnd WithsortRange.Copybecomes
With mobileSheet For currentRow = 2 To finalRow .... Next currentRow With .Range(.Cells(2, 16), .Cells(finalRow, 16)) .Sort Key1:=.Cells(2, 16), Order1:=xlAscending, header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal .Copy End WithEnd WithSummary
all what above can lead to the following refactoring ofSub bov_mobile()
Option ExplicitPrivate Sub bov_mobile() Dim currentRow As Long, finalRow As Long Dim mobileSheet As Worksheet: Set mobileSheet = ThisWorkbook.Sheets("Mobile") Dim bovSheet As Worksheet: Set bovSheet = ThisWorkbook.Sheets("Mod_Bov") With mobileSheet .Columns("P").ClearContents '<-- ClearContents() is faster than Clear(), if you don't bother formatting finalRow = .Cells(.Rows.Count, 1).End(xlUp).Row For currentRow = 2 To finalRow With .Cells(currentRow, 15) If .Text = "BOV" Or .Text = "BOV VMF" Then .Parent.Cells(currentRow, 1).Value = Join(.Offset(, -13).Resize(, 14), ";") End With Next currentRow With .Range(.Cells(2, 16), .Cells(finalRow, 16)) '<-- this is your "SortRange" .Sort Key1:=.Cells(2, 16), Order1:=xlAscending, header:=xlGuess, _ OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ DataOption1:=xlSortNormal bovSheet.Cells(bovSheet.Rows.Count, 1).End(xlUp).Offset(1).Resize(.Rows.Count, .Columns.Count).Value = .Value End With End WithEnd SubYou mustlog in to answer this question.
Explore related questions
See similar questions with these tags.


