inefficient code

Public Schedule Face-to-Face & Virtual Instructor-Led Training - View dates & book

Forum home » Delegate support and help forum » Microsoft Excel VBA Training and help » Inefficient code

Inefficient code

resolvedResolved · 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 courses

 

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
What does 'Resolved' mean?

Any suggestions, questions or comments? Please post in the Improve the forum thread.


 

Excel tip:

Turn Function tooltips on and off

Excel 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!

However, Function tooltips can become annoying. To turn them off, choose Tools|Options. and select the General tab. Then, untick the Function tooltips box and choose OK.

View all Excel hints and tips


Server loaded in 0.08 secs.