VBA Macro Stops/Hangs Excel after about 4000 Iterations - excel

I am posting this on behalf of someone else. Hoping I learn something in the process.
One of my team members is working on an excel macro that loops through the rows in a spreadsheet that contains over 14,000 rows. With each loop, it moves relevant data into a new tab within the workbook. The loop completes successfully unless we use the LastRow variable, or if we tell it to go for more than 400-4500 rows, then it crashes or hangs without any useful error info. The behavior does not change on different machines. We are using Excel 2016 to run the macro. I wanted to share the code with you to see if there is something that is causing it to hang (But why would it work fine for up to 4000 rows, and then quit beyond? I suspect memory issues to be the cause...)
I am sorry if this is answered elsewhere, I am not experienced enough to recognize if certain suggestions apply to this particular code.
Here is the code:
Function SheetExists(shtName As String, Optional wb As Workbook) As Boolean
Dim sht As Worksheet
If wb Is Nothing Then Set wb = ThisWorkbook
On Error Resume Next
Set sht = wb.Sheets(shtName)
On Error GoTo 0
SheetExists = Not sht Is Nothing
End Function
Sub SortProductionIntoWorkcenters()
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
LastRow = Worksheets("TL Production").Cells.SpecialCells(Type:=XlCellType.xlCellTypeLastCell).Row
FirstRow = 3
Dim rng As Range, cel As Range
'The next line that says Cells(LastRow, 4)) is where I can change how may iterations the loop will process
Set rng = Worksheets("TL Production").Range(Cells(FirstRow, 4), Cells(LastRow, 4))
Dim SheetName As String
Dim r As Integer
r = 2
For Each cel In rng
Worksheets("TL Production").Select
If Cells(cel.Row, cel.Column) = "" Then
Cells(cel.Row, cel.Column) = "EMPTY"
End If
SheetName = Worksheets("TL Production").Cells(cel.Row, 4).Value
SheetName = Replace(SheetName, "/", " ")
If Not SheetExists(SheetName) Then
Worksheets.Add.Name = SheetName
End If
Worksheets("TL Production").Rows(cel.Row).Cut
Do While r > 0
If IsEmpty(Worksheets(SheetName).Cells(r, 1)) Then
Worksheets(SheetName).Rows(r).Insert shift:=xlDown
r = 2
Exit Do
End If
r = r + 1
Loop
Next cel
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
'MinutesElapsed = Format((Timer - StartTime) / 86400, "hh:mm:ss")
' MsgBox "This code ran successfully in " & MinutesElapsed & " minutes", vbInformation
End Sub

Not an answer, but you would really benefit from simplifying your code. Eg:
For Each cel In rng
Worksheets("TL Production").Select
If cel = "" Then
cel = "EMPTY"
End If
SheetName = cel
etc...

Although I'm not entirely sure what the real issue in your code is (could very well be memory related), I see a couple of things that can improve your code, as well as its performance. See the bottom of the post for my proposal of a revised version of your code.
For Each cel In rng
Worksheets("TL Production").Select
If Cells(cel.Row, cel.Column) = "" Then
Cells(cel.Row, cel.Column) = "EMPTY"
End If
Executing .Select every single loop slows down your code drastically, as each .rows(r).Insert seems to change to another sheet. So your code forces Excel to constantly switch Worksheets. Redrawing the screen is orders of magnitude slower than performing calculations or reading some values from the sheet.
This can be further mitigated by completely switching off screen updating:
Application.ScreenUpdating = False
ws.Select
For Each cel In rng.Cells
...
Next cel
Application.ScreenUpdating = True
As mentioned by #PatrickHonorez, Cells(cel.Row, cel.Column) is a little bit overdoing it. It's a more complicated way of referencing cel - so why not use that directly? :) It also has the pitfall of not necessarily returning the correct cell, due to not being fully referenced. (Cells actually means ActiveWorkbook.ActiveSheet.Cells, so if your Workbook/Sheet change due to whatever reason, your script suddenly runs into trouble.)
If cel.Value = "" Then
cel.Value = "EMPTY"
End If
As mentioned in a comment by #dwirony, the While r > 0 condition in the Do Loop isn't really doing anything. There is no path through your code that allows for r < 2. Also, the way this loop is constructed is the major contributor to the macro's slow execution. (Several thousand rows in the original sheet means we enter this particular loop the equally often, and each time it has to count a little higher, due to the target sheets growing.)
I think this would be a good place to use a dictionary to store the number of the last row you inserted:
Do While r > 0
DoEvents
If IsEmpty(Worksheets(SheetName).Cells(r, 1)) Then
Worksheets(SheetName).Rows(r).Insert shift:=xlDown
dict(SheetName) = r
Exit Do
End If
r = r + 1
Loop
Generally:
Use Option Explicit at the top of any module. It will make your life easier. (Thus the compiler will force you to declare each and every variable you use. This makes your code more concise and eliminates potential typos, among other benefits.) You can also make this the standard in the VBA IDE's options.
If the sheets modified by your macro contain formulas you can deactivate automatic recalculation (if not already set to manual) with Application.Calculation = xlCalculationManual - this will in some cases further reduce execution times. If you want to set it back to automatic afterwards, use Application.Calculation = xlCalculationAutomatic.
Add a line DoEvents to each and every Do Loop you don't perfectly trust. This will allow you stop/pause the macro if it turns out to be an (almost) infinite loop.
My revised version, I tested it with about 6000 rows to be distributed to 3 different worksheets. It took about 2min to complete. Although rows with more data might take longer than my quick mock-up.
Sub SortProductionIntoWorkcenters()
Dim StartTime As Double
Dim SecondsElapsed As Double
Dim LastRow As Long, FirstRow As Long
Dim Ws As Worksheet
Dim Dict As Scripting.Dictionary
StartTime = Timer
Set Dict = New Scripting.Dictionary
Set Ws = Worksheets("TL Production") ' Set the reference to the starting sheet once and then use that
LastRow = Ws.Cells.SpecialCells(Type:=XlCellType.xlCellTypeLastCell).Row
FirstRow = 3
Dim rng As Range, cel As Range
'The next line that says Cells(LastRow, 4)) is where I can change how may iterations the loop will process
Set rng = Ws.Range(Cells(FirstRow, 4), Cells(LastRow, 4))
Dim SheetName As String
Dim r As Long ' Use Long datatype here to prevent integer overflow
r = 2
Application.ScreenUpdating = False
For Each cel In rng.Cells ' make explicit that we are iterating over all cells in range
If cel.Value = "" Then
cel.Value = "EMPTY"
End If
SheetName = Ws.Cells(cel.Row, 4).Value
SheetName = Replace(SheetName, "/", " ")
If Not SheetExists(SheetName) Then
Worksheets.Add.Name = SheetName
End If
Ws.Rows(cel.Row).Cut
If Dict.Exists(SheetName) Then r = Dict(SheetName)
Do
DoEvents
If IsEmpty(Worksheets(SheetName).Cells(r, 1)) Then
Worksheets(SheetName).Rows(r).Insert shift:=xlDown
Dict(SheetName) = r + 1 ' Add one, as the row r is not empty by defition
Exit Do
End If
r = r + 1
Loop
Next cel
Application.ScreenUpdating = True
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
'MinutesElapsed = Format((Timer - StartTime) / 86400, "hh:mm:ss")
' MsgBox "This code ran successfully in " & MinutesElapsed & " minutes", vbInformation
End Sub

Related

How to avoid duplication in Excel VBA Macro

Beginner here and I managed to modify a code to extract data from a sheet and copy and paste them to other sheets. Problem is when I click run Macro or the button assigned to the Macro, it is duplicating rows again. Please help me to avoid the duplication.
TIA
Sub UpdateHistory()
Dim wsData As Worksheet, wsCostCode As Worksheet
Dim LastRow As Long, NextRow As Long, i As Long
Dim CostCode As String
Dim Company As String
Dim Invoice As String
Dim Price As Double
Application.ScreenUpdating = False
Set wsData = Sheets("Signed Invoices")
LastRow = wsData.Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To LastRow
CostCode = wsData.Range("A" & i).Value
Company = wsData.Range("B" & i).Value
Invoice = wsData.Range("C" & i).Value
Total = wsData.Range("D" & i).Value
If WorksheetExists(CostCode) = True Then
Set wsCostCode = Sheets(CostCode)
NextRow = wsCostCode.Cells(Rows.Count, 1).End(xlUp).Row + 1
wsCostCode.Range("A" & NextRow).Value = CostCode
wsCostCode.Range("B" & NextRow).Value = Company
wsCostCode.Range("C" & NextRow).Value = Invoice
wsCostCode.Range("D" & NextRow).Value = Total
Else
wsData.Range("A1:D1").Copy
Worksheets.Add(After:=Sheets(Sheets.Count)).Name = CostCode
ActiveSheet.Cells(1, 1).PasteSpecial
ActiveSheet.Range("A2").Value = CostCode
ActiveSheet.Range("B2").Value = Company
ActiveSheet.Range("C2").Value = Invoice
ActiveSheet.Range("D2").Value = Total
End If
Next
Application.CutCopyMode = False
Sheets("Signed Invoices").Select
Application.ScreenUpdating = True
End Sub
Function WorksheetExists(shtName As String, Optional wb As Workbook) As Boolean
Dim sht As Worksheet
If wb Is Nothing Then Set wb = ThisWorkbook
On Error Resume Next
Set sht = wb.Sheets(shtName)
On Error GoTo 0
WorksheetExists = Not sht Is Nothing
End Function
When you find that your code isn't doing what you expect, try stepping through it line-by-line and see exactly where and when it goes wrong. You can do this by pressing F8 while your cursor is anywhere in your macro. I also recommend commenting out Application.ScreenUpdating = False until your code is working as expected. Otherwise, following the code's behavior can become difficult when the code is supposed to write things to worksheets.
You've found that your code is duplicating entries. Let's check all places in your macro that write data to the sheet. There is only one place: inside your For i = 2 to LastRow loop. Because you have set up a loop, you are expecting (or at least preparing) for this block of code to run more than once. The next question should be, why is the data not changing between two iterations like you're expecting?
Check that Else block of code. It seems like you copy the headers, add a new sheet, and then use the ActiveSheet to specify which sheet to write the data. Is ActiveSheet the sheet you think it is? (Very easy to verify with line-by-line debugging.) If you really want to use ActiveSheet, make sure the sheet you expect to be active is active with Worksheets(Worksheets.Count).Activate. This will activate the last worksheet, which is where you want to write your data.
Try stepping line-by-line through your code and see if this is correct before modifying your code.

SOLVED - ]Read data and copy to current workbook

With below code, no errors are displayed, the read file opens but it seems not data is copied.
I am trying to copy only a number of columns, but it seems nothing is been copied to current workbook.
Any help would be appreciated as I am very new with VBA
Sub ReadDataFromCloseFile()
On Error GoTo ErrHandler
'stop screen update
Application.ScreenUpdating = False
Dim src As Workbook
Dim sTheSourceFile As String
sTheSourceFile = "C:\Users\grmn\Desktop\testreadfile.xlsx"
Set src = Workbooks.Open(sTheSourceFile, True, True)
Dim iRowsCount As Long
'source of data
With src.Worksheets("Sheet1")
iRowsCount = .Range("A1:A" & .Cells(Rows.Count, "A").End(xlUp).Row).Rows.Count
End With
Dim iCnt As Long
'destination sheet thisWorkbook.sheet("rapport")
For iCnt = 1 To iRowsCount
Worksheets("rapport").Range("A" & iCnt).Formula = src.Worksheets("Sheet1").Range("A" & iCnt).Formula
Worksheets("rapport").Range("F" & iCnt).Formula = src.Worksheets("Sheet1").Range("B" & iCnt).Formula
Next iCnt
'close but not overide source file (src).
src.Close False
Set src = Nothing
ErrHandler:
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
No worries being new, we all were at some point.
The first part of your code 'source of data doesn't work as intended. iRowsCount is an Integer and not an Array. To make use of an array, as you seemingly tried to do, you should use
Dim iRowsCount(8) As Long
With src.Worksheets("Sheet")
iRowsCount(1) = .Range("A1:A" & .Cells(Rows.Count, "A").End(xlUp).Row).Rows.Count
' ...
End With
' ...
If you use an Integer only the last row will be assigned. So if "AT", for some reason, has 5 rows, iRowsCount will be 5. Nothing else. Not accounting for "AQ" or "AS".
But in your case, Integer/Long would probably suffice if all rows have the exact same count. One assignment would be enough then.
Regarding .Formula - are you really trying to write formulas? Have you tried .value instead?
And, what may be the crux of the matter, try Worksheets("rapport").Save or Worksheets("rapport").SaveAs at the end of your function.
(Haven't tested it on my end so far.)
Additionally, please remember to set Exit Sub (or Exit Function respectively, if a Function) to avoid executing ErrHandler if no error occurs.
(Sorry, I'm new to Stackoverflow, so I can't write comments as of yet.)
(Edit: Thanks for the reminder, #FunThomas, Integer is only -32768 to 32767. Long is 8 bytes.)

Assigning Macro with ParamArray: Formula is Too Complex to add to the Object

I have a macro (below) that inserts a new row into an un-defined number of Named ranges using ParamArray, it works fine except for when I try to assign the macro with more than 5-6 arguments I get a message box that says "Formula Too Complex to Assign To Object" (see picture above)
(see assignment string below)
'InsertNewRow "ServiceCrewDay_EmployeeList", "SAP_SCD_InPool", "SAP_SCD_OutPool", "SAP_SCD_SecondaryIn", "SAP_SCD_SecondaryOut", "SAP_SCD_ORD","SAP_SCD_THF","SAP_SCD_LH", "SAP_SCD_LH"'
Macro:
Sub InsertNewRow(ParamArray args() As Variant)
Dim ans: ans = MsgBox("WARNING: " & vbNewLine _
& "Action Cannot be undone!" & vbNewLine & "Continue?", vbYesNo, "Warning!")
If ans = vbNo Then: Exit Sub
Call HaltOperations
Call ActiveSheet.Unprotect()
Call Sheets("SAP Timesheet").Unprotect()
On Error GoTo OnError_Exit
'Loop and Check All Named Ranges Exist Before Proceeding
For Each a In args
If RangeExists(a) = False Then
MsgBox ("Named Range: " & a & " Not Defined!" & vbNewLine & "Operation Cancelled")
Exit Sub
End If
Next a
Dim rng As Range
'ADD ROW TO EACH NAMED INPUT RANGE
For Each a In args
Set rng = Range(a)
With rng
.Rows(.Rows.count).EntireRow.Insert
.Rows(.Rows.count - 2).EntireRow.Copy
.Rows(.Rows.count - 1).EntireRow.PasteSpecial (xlPasteFormulasAndNumberFormats)
On Error Resume Next: .Rows(.Rows.count - 1).EntireRow.PasteSpecial (xlPasteFormats)
End With
Next a
On Error GoTo OnError_Exit
'ADJUST HEIRACHY NUMBERS ON FIRST INPUT RANGE (MANNING TAB)
Set rng = Range(args(0))
Dim col As Integer
col = rng.Column
Cells(rng.Row + rng.Rows.count - 2, col).Offset(0, -1).Value _
= Cells(rng.Row + rng.Rows.count - 3, col).Offset(0, -1).Value + 1
Cells(rng.Row + rng.Rows.count - 1, col).Offset(0, -1).Value _
= Cells(rng.Row + rng.Rows.count - 3, col).Offset(0, -1).Value + 2
Call ResumeOperations
Application.CutCopyMode = False
Call ActiveSheet.Protect()
Call Sheets("SAP Timesheet").Protect()
Exit Sub
OnError_Exit:
Call ResumeOperations
Application.CutCopyMode = False
Call ActiveSheet.Protect()
Call Sheets("SAP Timesheet").Protect()
End Sub
Private Function RangeExists(rng As Variant) As Boolean
Dim Test As Range
On Error Resume Next
Set Test = Range(rng)
RangeExists = Err.Number = 0
End Function
Private Sub HaltOperations()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
Application.Calculation = xlCalculationManual
End Sub
Private Sub ResumeOperations()
ResumeOps:
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
End Sub
The Macro itself runs as expected it's just the assigning the named ranges that is causing the issue.
is there a better way to do this?
or is there a way to get around the Formula is too complex method?
and if there is will that need to be done on all end user pc's or just on mine and the settings will carry over?
What I have thought about doing was just taking in 2 Named ranges and then for the following ranges Just offsetting those by the Row Count of the previous range so if Range2 = Sheets().Range("A1:A10") then Range3 = Range2.Offset(Range2.Rows.Count,0) then the assingment input would only need to be Range1 as string, Range2 as string, NumberOfExtraRanges as integer the reason I need atleast two ranges is because every range after range 1 is on a different tab and is essentially a raw data version of all pay info hours etc. in the first tab which will be Range1_EmployeeList
which I will play around with while I wait for a response.
TIA
Not a Complete answer but I did find that inside the ParamArray I could just assign One Input Range using a , to seperate each defined range. I haven't tested the limitations doing it this way but it does seem to atleast let me use a few extra inputs.
Example (Not Working):
Note: Each Defined Range is a Separate Input
'InsertNewRow "ServiceCrewDay_EmployeeList", "SAP_SCD_InPool" ," SAP_SCD_OutPool","SAP_SCD_SecondaryIn", "SAP_SCD_SecondaryOut"'
Example (Working):
Note Each Defined Range is passed as 1 input
'InsertNewRow "ServiceCrewDay_EmployeeList", "SAP_SCD_InPool, SAP_SCD_OutPool,SAP_SCD_SecondaryIn,SAP_SCD_SecondaryOut"'

Table refresh vba excel Call procedure from another procedure Error Code 1004

I have a call procedure to clear contents of tables across multiple worksheets.
This procedure is invoked only from the 2nd sheet of the workbook. When I invoke this, I am getting Error 1004 "Application-defined or Object-defined error".
Below is the parent code base invoking the sub procedure:
Sub ValidateData_BDV1()
On Error Resume Next
Err.Clear
'''''Define Variables'''''''''
Dim mySheet As Worksheet
Dim mySheetName As String
Dim bdvName As Variant
Dim sqlQuery As String
Dim connectStr As String
Dim wsMatch As Worksheet
Dim myWorkbook As Workbook: Set myWorkbook = ThisWorkbook
'''''''''Set Variables''''''''
cancelEvent = False
Set mySheet = ActiveSheet 'Sets mySheet variable as current active sheet
mySheetName = mySheet.Name
driverName = mySheet.Range("B1").Value2 'Get the value of the TDV driver
' MsgBox driver
dataSourceName = mySheet.Range("B3").Value2 'Get the data source name for the published TDV database
' MsgBox dataSourceName
schemaName = mySheet.Range("B5").Value2 'Get the schema name of the published tdv view
bdvName = mySheet.Range("B6").Value2 'Get the name of the published BDV
''''''''''Refresh data across sheets'''''''''''''
Application.ScreenUpdating = False 'Prevent screen flickering while doing the refresh
'''''''''''''''''''''''''''''''''''''''
''''''''''''Call sub procedure'''''''''
Call ClearTableContents
''''''''''''''''''''''''''''''''''''
mySheet.Activate
Application.ScreenUpdating = True 'Prevent screen flickering while doing the refresh
''''''''Show User id and Password box'''''''''
If Len(Uid) < 1 Or Len(Password) < 1 Then
UserForm1.Show
End If
If (cancelEvent = True) Then
Exit Sub
End If
............
............perform some task with error handling
Below is the code base of the called Sub
Sub ClearTableContents()
Dim wrksht As Worksheet
Dim objListObj As ListObjects
Dim tableName As String
Dim ActiveTable As ListObject
Dim rowCount As Integer
Dim colCount As Integer
Dim i As Integer
Dim j As Integer
'''''Iterate through the Bdv1, bdv2 and Match sheets. Set default table sizes for each
sheet'''''''''
For j = 2 To 4
If (j = 2) Or (j = 3) Then
rowCount = 5
colCount = 6
ElseIf (j = 4) Then
rowCount = 5
colCount = 9
End If
Application.ScreenUpdating = False 'Prevent screen flickering while doing the refresh
Set wrksht = ActiveWorkbook.Worksheets(j)
Set objListObj = wrksht.ListObjects 'Get list of tables objects from the current sheet
'''''''Iterate through the tables in the active worksheet''''''''''''''
For i = 1 To objListObj.Count
tableName = objListObj(i).Name
Set ActiveTable = wrksht.ListObjects(tableName)
On Error Resume Next
''''''For each table clear the contents and resize the table to default settings''''''''''''
With wrksht.ListObjects(i)
.DataBodyRange.Rows.Clear
.Range.Rows(rowCount & ":" & .Range.Rows.Count).Delete
.HeaderRowRange.Rows.ClearContents
.HeaderRowRange.Rows.Clear
.Range.Columns(colCount & ":" & .Range.Columns.Count).Delete
.Resize .Range.Resize(rowCount, colCount)
End With
wrksht.Columns("A:Z").AutoFit
Next i
Next j
ThisWorkbook.Worksheets(2).Activate '''set the active sheet to the sheet number 2
Application.ScreenUpdating = True 'Prevent screen flickering while doing the refresh
Exit Sub
'Error Handling
NoTableSelected:
MsgBox "There is no Table currently selected!", vbCritical
End Sub
Please help in resolving the issue.
If I execute as independent macro on click of the button, it works perfectly well.
I am going to post this as an "answer", since I think it may at least help, if not solve, your issue.
Clearing tables (list objects) via VBA code can be a little tricky, and I learned this hard way. I developed and have been using the below function for quite some time and it works like a charm. There are comments to explain the code in the function.
Sub clearTable(whichTable As ListObject)
With whichTable.DataBodyRange
'to trap for the bug where using 'xlCellTypeConstants' against a table with only 1 row and column will select all constants on the worksheet - can't explain more than that its a bug i noticed and so did others online
If .rows.count = 1 And .columns.count = 1 Then
If Not .Cells(1, 1).HasFormula Then .Cells(1, 1).ClearContents
Else
'my tables often have formulas that i don't want erased, but you can remove if needed
On Error Resume Next
.SpecialCells(xlCellTypeConstants).ClearContents
On Error GoTo 0
End If
'remove extra rows so table starts clean
Dim rowCount As Long
rowCount = .rows.count
If rowCount > 1 Then .rows("2:" & rowCount).Delete 'because you can't delete the first row of the table. it will always have 1 row
End With
End Sub
Call the procedure like this:
Dim lo as ListObject
For each lo in Worksheets(1).ListObjects
clearTable lo
next
Commented line to make my code work
.Range.Columns(colCount & ":" &
.Range.Columns.Count).Delete

How to efficiently run through rows of data to match a criterion using excel VBA?

Normally what I do on my program is using logic like:
Total_rows_worksheet = Worksheets("Sample").Range("A" & Rows.Count).End(xlUp).Row
For i = 2 to Total_rows_worksheet
If Worksheets("Sample").Cells(i,1)=Criteria Then
[Perform this action]
End If
Next i
However, when the number of rows get large, code that runs through the entire sheet gets a bit slow. Also, I remember my programmer friend telling me that it is a common mistake for beginner programmers to run through all the data. The correct way according to him was to point to the rows of interest, but I do not know exactly how to do that in Excel.
Disable screen updating and calculation before the loop and it will increase the speed immensely.
Sub testing()
'Disable screen updating and calculation
Dim uRange As Range
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Total_rows_worksheet = Worksheets("Sample").Range("A" & Rows.Count).End(xlUp).Row
For i = 1 To Total_rows_worksheet
If Worksheets("Sample").Cells(i, 1) = "OK" Then
If uRange Is Nothing Then
Set uRange = Worksheets("Sample").Cells(i, 1)
Else
Set uRange = Union(uRange, Worksheets("Sample").Cells(i, 1))
End If
End If
Next i
uRange.Value = "THIS USED TO SAY OK"
'Enable screen updating and calculation when done
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Edit 1: Depending on the task, another way to speed things up is to change all at once by adding ranges to a Union.
Note: Screen updating should be the first to be disabled and the last to be enabled when going for speed in Excel-VBA. There are other things that can be disable like Events that also help if you have event specific triggers.
You might use something similar as:
Public roww
Sub Main()
Dim sht
roww = InputBox("Start Row", "Row to start", "", 8000, 6000)
If roww <> "" Then
Set sht = Sheets("Your sheet")
'this is a validation
Do Until sht.Cells(roww, "A").Value = ""
'this is other optional validation
If sht.Cells(roww, "Y").Text <> "" Then
[Perform this action]
roww = roww + 1
Else
roww = roww + 1
End If
Loop
Else
End If
MsgBox ("The Process has been completed")
End Sub
It should stop until the the last row with data.

Resources