98.7% Of all customers recommend us, we're so confident about our results we publish all reviews and stats
View Live Stats View ReviewsForum home » Delegate support and help forum » Microsoft Excel VBA Training and help » Inefficient code
Inefficient code
Resolved · Medium Priority · Version 2016
Diane has attended:
Excel VBA Introduction course
Excel VBA Intermediate course
Excel VBA Advanced course
Inefficient code
Hi
I have written some code (pasted below), my issue is that it is very cludgey in performance during the read write loop. It is reading in my sample 120 lines and writing 70 output lines. This is taking in the region of 30 secs. I think I can guess why, but given what I want to do I'm not sure how I can rectify. Any pointers would be helpful. I think the probable cause is that I am reading from one workbook and writing to a different workbook. I do need to write to another book because the O/P file will be used to import into our financial system. I have pasted the full code beneath. It's the LoopThruProjects that is the time gobbler.
Option Explicit
Private ReportWb As Workbook 'Output workbook
Private ProjectWb As Workbook 'Source project details
'Private iReport As Integer
Private iLastReportedRow 'this is not the same as iLastRow, this gets last reported row, not last project row
Private iLastRow As Integer 'Last project row in source workbook
Private iRowCounter As Integer 'Counter for input row loop
Private iWriteCounter ' This counter is used to ensure no blank lines written to O/P, blank lines occur if iRowCounter used
Sub Main()
' This vb
' calls sub to create a new work book to write the project detail report to (closes if already open)
' calls sub to create the header row in the new wb
' Calls a sub to Loop down the source until it hits a blank cell, for each project row, details are copied to target wb
' Calls sub to tidy up the output workbook
Application.ScreenUpdating = False
' Dim ReportWs As Worksheet
'set variables
Set ProjectWb = ThisWorkbook
iLastRow = Cells(Rows.Count, 2).End(xlUp).Row 'get last row for column B (all projects added)
iLastReportedRow = Cells(Rows.Count, 1).End(xlUp).Row ' gets last row for projects reported
iLastReportedRow = iLastReportedRow + 1 'Set counter to next empty reported row
Call PrepareOutputfile
Call CreateHeader 'Create header in target wb
ProjectWb.Activate 'make source wb active
Cells(iLastReportedRow, 2).Select
iWriteCounter = 2
Call LoopThruProjects
Call TidyupOutput
ProjectWb.Activate
'ReportWb.Close
End Sub
Sub PrepareOutputfile()
'Close if open then save as output workbook
Application.DisplayAlerts = False
On Error Resume Next
Workbooks("ReportForEvision").Close
Workbooks.Add 'Add workbook
ActiveWorkbook.SaveAs Filename:="H:\ReportForEvision.xlsx"
Application.DisplayAlerts = True
Set ReportWb = Workbooks("ReportForEvision.xlsx")
End Sub
Sub LoopThruProjects()
For iRowCounter = iLastReportedRow To iLastRow 'loop through each source project row
If ActiveCell.Offset(0, 5) <> "Archived" And ActiveCell.Offset(0, 5) <> "Cancelled" _
And ActiveCell.Offset(0, 5) <> "Tender" Then '
ActiveCell.Offset(0, -1) = "Project Reported"
Call CopyCells 'copy source details to target wb
iWriteCounter = iWriteCounter + 1
Else ' has not been reported but does not have a valid status
ActiveCell.Offset(0, -1) = "Project Status - Ignored for Evision" 'Not an active status, do not report
End If
ActiveCell.Offset(1, 0).Select
Next iRowCounter
ReportWb.Activate
Cells.Select
Cells.EntireColumn.AutoFit
End Sub
Sub CreateHeader()
' This called sub creates the target wb header row
Application.ScreenUpdating = False
[a1].Select
Dim MyCounter As Integer
MyCounter = 1
Do While MyCounter < 13
Select Case MyCounter
Case Is = 1
ActiveCell = "Project Name"
Case Is = 2
ActiveCell = "Completion Date"
Case Is = 3
ActiveCell = "Agreed Contract Sum"
Case Is = 4
ActiveCell = "Contract Period"
Case Is = 5
ActiveCell = "Possession Date"
Case Is = 6
ActiveCell = "DLP Period"
Case Is = 7
ActiveCell = "DLP Starts"
Case Is = 8
ActiveCell = "DLP Expiry"
Case Is = 9
ActiveCell = "LOI"
Case Is = 10
ActiveCell = "LOI Cap"
Case Is = 11
ActiveCell = "Retention"
Case Is = 12
ActiveCell = "Project Status"
End Select
MyCounter = MyCounter + 1
ActiveCell.Offset(0, 1).Select
Loop
Cells.Select
Cells.EntireColumn.AutoFit
Range("B:B,E:E,G:G,H:H").Select
Range("H1").Activate
Selection.NumberFormat = "dd/mm/yyyy;@"
Columns("C:C").Select
Selection.NumberFormat = "£#,##0"
Columns("D:D").Select
Selection.NumberFormat = "0"
[a2].Select
End Sub
Sub CopyCells()
' This called sub copies the project details to ReportForEvision
Application.ScreenUpdating = False
Dim iCopy As Integer
Dim Target
Set Target = ReportWb.Worksheets("sheet1")
Cells(iRowCounter, 4).Copy
Target.Cells(iWriteCounter, 1).PasteSpecial xlPasteValues
Range("b" & iRowCounter).Offset(0, 13).Copy
Target.Range("b" & iWriteCounter).PasteSpecial xlPasteValues 'Completion Date
Range("b" & iRowCounter).Offset(0, 14).Copy
Target.Range("c" & iWriteCounter).PasteSpecial xlPasteValues 'Contract Sum
Range("b" & iRowCounter).Offset(0, 17).Copy
Target.Range("d" & iWriteCounter).PasteSpecial xlPasteValues 'Contract Period
Range("b" & iRowCounter).Offset(0, 18).Copy
Target.Range("e" & iWriteCounter).PasteSpecial xlPasteValues 'Possession Date
Range("b" & iRowCounter).Offset(0, 19).Copy
Target.Range("f" & iWriteCounter).PasteSpecial xlPasteValues 'DLP in weeks
Range("b" & iRowCounter).Offset(0, 20).Copy
Target.Range("g" & iWriteCounter).PasteSpecial xlPasteValues 'DLP Starts
Range("b" & iRowCounter).Offset(0, 21).Copy
Target.Range("h" & iWriteCounter).PasteSpecial xlPasteValues 'DLP Expiry
Range("b2").Offset(0, 22).Copy
Target.Range("i" & iWriteCounter).PasteSpecial xlPasteValues 'LOI
Range("b" & iRowCounter).Offset(0, 23).Copy
Target.Range("j" & iWriteCounter).PasteSpecial xlPasteValues 'LOI Cap
Range("b" & iRowCounter).Offset(0, 24).Copy
Target.Range("k" & iWriteCounter).PasteSpecial xlPasteValues 'Retention
Range("b" & iRowCounter).Offset(0, 5).Copy
Target.Range("l" & iWriteCounter).PasteSpecial xlPasteValues 'Project Status
End Sub
Sub TidyupOutput()
'Put borders around cells
Dim xlEdgeType As Variant
For Each xlEdgeType In Array(xlEdgeLeft, xlEdgeTop, xlEdgeRight, _
xlEdgeBottom, xlInsideVertical, xlInsideHorizontal)
Range("A1").CurrentRegion.Select
With Selection.Borders(xlEdgeType)
.LineStyle = xlContinuous
.ColorIndex = xlAutomatic
.TintAndShade = 0
.Weight = xlThin
End With
Next xlEdgeType
[a1].Select
End Sub
RE: Inefficient code
Hi Diane,
Thank you for the forum question.
VBA takes time to run. Especially Copy and Paste and loops take time.
The code below I do not understand. Why not
Range("a1")="Project Name"
Range("B1")="Completion Date"
Range("c1")="Agreed Contract Sum"
and so on with the rest of the headings.
Sub CreateHeader()
' This called sub creates the target wb header row
Application.ScreenUpdating = False
[a1].Select
Dim MyCounter As Integer
MyCounter = 1
Do While MyCounter < 13
Select Case MyCounter
Case Is = 1
ActiveCell = "Project Name"
Case Is = 2
ActiveCell = "Completion Date"
Case Is = 3
ActiveCell = "Agreed Contract Sum"
Case Is = 4
ActiveCell = "Contract Period"
Case Is = 5
ActiveCell = "Possession Date"
Case Is = 6
ActiveCell = "DLP Period"
Case Is = 7
ActiveCell = "DLP Starts"
Case Is = 8
ActiveCell = "DLP Expiry"
Case Is = 9
ActiveCell = "LOI"
Case Is = 10
ActiveCell = "LOI Cap"
Case Is = 11
ActiveCell = "Retention"
Case Is = 12
ActiveCell = "Project Status"
End Select
MyCounter = MyCounter + 1
ActiveCell.Offset(0, 1).Select
Loop
Change:
Cells.Select
Cells.EntireColumn.AutoFit
To:
Columns("A:L").autofit
Change:
Range("B:B,E:E,G:G,H:H").Select
Range("H1").Activate
Selection.NumberFormat = "dd/mm/yyyy;@"
To:
Range("B:B,E:E,G:G,H:H").NumberFormat = "dd/mm/yyyy;@"
Change:
Columns("C:C").Select
Selection.NumberFormat = "£#,##0"
To:
Columns("C:C").NumberFormat = "£#,##0"
Change:
Columns("D:D").Select
Selection.NumberFormat = "0"
To:
Columns("D:D").NumberFormat = "0"
I hope this will speed up your macro.
Kind regards
Jens Bonde
Microsoft Office Specialist Trainer
Tel: 0207 987 3777
STL - https://www.stl-training.co.uk
98%+ recommend us
London's leader with UK wide delivery in Microsoft Office training and management training to global brands, FTSE 100, SME's and the public sector
RE: Inefficient code
Thanks Jen and thank you for the pointer on my coding. I am a newbie to VB and this is my first piece of 'real' vb (apart from training of course), so any feedback is very welcome. I do struggle with the nuances of the object model.
Kind regards
Diane
RE: Inefficient code
Hi Jen
I implemented those code changes thanks, whilst they are better coding, unfortunately my underlying problem still hasn't changed. It is still very slow. 30+ secs is very slow for just 120 rows. If I had 1000's of rows it would take all day. Is there a smarter way I can loop through these records rather than the method I am using?
I also tried a version of this which wrote to another worksheet rather than another workbook to see if that was my issue, but that made no difference.
Kind Regards Diane
RE: Inefficient code
Hi Diane,
The next step is to identify which part of the code slows down the macro.
Create variables:
Dim StartTime1 as double
Dim StartTime2 as double
Dim StartTime3 as double
StartTime1 = Timer
Your Code
Range("N1") = Timer - StartTime1
StartTime2 = Timer
Your Code
Range("N2") = Timer - StartTime2
StartTime3 = Timer
Your Code
Range("N3") = Timer - StartTime3
Use as many timers you need to get and idea of which part of your code takes time.
The reason can also be other issues in your workbook.
Please let me know which part of the code takes a lot of time to run.
Kind regards
Jens Bonde
Microsoft Office Specialist Trainer
Tel: 0207 987 3777
STL - https://www.stl-training.co.uk
98%+ recommend us
London's leader with UK wide delivery in Microsoft Office training and management training to global brands, FTSE 100, SME's and the public sector
RE: Inefficient code
Hi Jens
I have put the timers in and the code which appears to be causing the problem is the copycells subroutine.
The For Loop and if statement is fine.
Kind regards
Diane
RE: Inefficient code
Hi Diane,
Change:
Cells(iRowCounter, 4).Copy
Target.Cells(iWriteCounter, 1).PasteSpecial xlPasteValues
Range("b" & iRowCounter).Offset(0, 13).Copy
Target.Range("b" & iWriteCounter).PasteSpecial xlPasteValues 'Completion Date
Range("b" & iRowCounter).Offset(0, 14).Copy
Target.Range("c" & iWriteCounter).PasteSpecial xlPasteValues 'Contract Sum
Range("b" & iRowCounter).Offset(0, 17).Copy
Target.Range("d" & iWriteCounter).PasteSpecial xlPasteValues 'Contract Period
Range("b" & iRowCounter).Offset(0, 18).Copy
Target.Range("e" & iWriteCounter).PasteSpecial xlPasteValues 'Possession Date
Range("b" & iRowCounter).Offset(0, 19).Copy
Target.Range("f" & iWriteCounter).PasteSpecial xlPasteValues 'DLP in weeks
Range("b" & iRowCounter).Offset(0, 20).Copy
Target.Range("g" & iWriteCounter).PasteSpecial xlPasteValues 'DLP Starts
Range("b" & iRowCounter).Offset(0, 21).Copy
Target.Range("h" & iWriteCounter).PasteSpecial xlPasteValues 'DLP Expiry
Range("b2").Offset(0, 22).Copy
Target.Range("i" & iWriteCounter).PasteSpecial xlPasteValues 'LOI
Range("b" & iRowCounter).Offset(0, 23).Copy
Target.Range("j" & iWriteCounter).PasteSpecial xlPasteValues 'LOI Cap
Range("b" & iRowCounter).Offset(0, 24).Copy
Target.Range("k" & iWriteCounter).PasteSpecial xlPasteValues 'Retention
Range("b" & iRowCounter).Offset(0, 5).Copy
Target.Range("l" & iWriteCounter).PasteSpecial xlPasteValues 'Project Status
End Sub
To:
Target.Cells(iWriteCounter, 1)=Cells(iRowCounter, 4)
Target.Range("b" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 13)
Target.Range("c" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 14)
Target.Range("d" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 17)
Target.Range("e" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 18)
Target.Range("f" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 19)
Target.Range("g" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 20)
Target.Range("h" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 21)
Target.Range("i" & iWriteCounter)=Range("b2").Offset(0, 22)
Target.Range("j" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 23)
Target.Range("k" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 24)
Target.Range("l" & iWriteCounter)=Range("b" & iRowCounter).Offset(0, 5)
I hope this will speed up your code.
Kind regards
Jens Bonde
Microsoft Office Specialist Trainer
Tel: 0207 987 3777
STL - https://www.stl-training.co.uk
98%+ recommend us
London's leader with UK wide delivery in Microsoft Office training and management training to global brands, FTSE 100, SME's and the public sector
RE: Inefficient code
Hi Jens
Well it is absolutely sizzling now. Over in a couple of seconds.
BTW I noticed I had forgotten to copy through the top cells line to all replace the offset as you had advised a few days ago, so I combined your previous suggestion with your latest solution and the code below is the Eusain Bolt of vb copy :) and it is easier on the eye to read.
Thanks very much, and also I was amazed to get a reply on a Sunday. You guys never fail to impress :)
target.Cells(iWriteCounter, "b") = Cells(iRowCounter, "o") 'Completion date
target.Cells(iWriteCounter, "c") = Cells(iRowCounter, "p") 'contract sum
target.Cells(iWriteCounter, "d") = Cells(iRowCounter, "s") 'Contract period
target.Cells(iWriteCounter, "e") = Cells(iRowCounter, "t") 'possession date
target.Cells(iWriteCounter, "f") = Cells(iRowCounter, "u") 'DLP period
target.Cells(iWriteCounter, "g") = Cells(iRowCounter, "v") 'DLP starts
target.Cells(iWriteCounter, "h") = Cells(iRowCounter, "w") 'DLP expiry
target.Cells(iWriteCounter, "i") = Cells(iRowCounter, "x") 'LOI
target.Cells(iWriteCounter, "j") = Cells(iRowCounter, "y") 'LOI cap
target.Cells(iWriteCounter, "k") = Cells(iRowCounter, "z") 'Retention
target.Cells(iWriteCounter, "l") = Cells(iRowCounter, "g") 'Project Status
target.Cells(iWriteCounter, "m") = Cells(iRowCounter, "c") 'Project Number
RE: Inefficient code
Hi Diane,
I am happy I could help you.
Kind regards
Jens Bonde
Microsoft Office Specialist Trainer
Tel: 0207 987 3777
STL - https://www.stl-training.co.uk
98%+ recommend us
London's leader with UK wide delivery in Microsoft Office training and management training to global brands, FTSE 100, SME's and the public sector
RE: Inefficient code
Good Morning Diane,
I hope that you are well.
I just told you yesterday that everything is possible when you use VBA.
A problem if you want to pin many locations on Google Map is, that you need to login to Google and you need to create your own map. You can upload a Excel address list and pin all the addresses from the list, but you can only share the map by using Facebook, Twitter, or Gmail.
I will be happy to send you a step by step guide how to do this, if you want the guide.
Have a great day.
Kind regards
Jens Bonde
Microsoft Office Specialist Trainer
Tel: 0207 987 3777
STL - https://www.stl-training.co.uk
98%+ recommend us
London's leader with UK wide delivery in Microsoft Office training and management training to global brands, FTSE 100, SME's and the public sector
RE: Inefficient code
Hi Jens
Firstly, thanks once again for a very enjoyable course. I learned a lot and am looking forward to applying my knowledge. I'm sure you will get some questions on the forum :)
Thank you for investigating the google maps question, I don't think I will go forward with it simply because of the distribution methods, but thanks anyway.
Kind Regards
Diane
Training information:
Welcome. Please choose your application (eg. Excel) and then post your question. Our Microsoft Qualified trainers will then respond within 24 hours (working days). Frequently Asked Questions
Any suggestions, questions or comments? Please post in the Improve the forum thread. |
Excel tip:Turn Function tooltips on and offExcel 2002 (XP) and Excel 2003 have the Function tooltips facility. When you type in a function name followed by a bracket, for example, =IF(, a yellow box appears beside the function name and lists the function's arguments. This is very useful when you can't quite remember the order of a function's arguments or what the arguments actually are! |