I have a macro that will take each value in a list, place it in a different sheet (which performs its own calculations) and returns certain values (like a summary sheet). I have created a looping macro to do this very action, but since there are about 6500 entries on the list, the macro executes at a very slow pace. I have turned off screen updating, and calculations have to be automatic, so I was wondering: is there any other way to speed up the macro?
Sub watchlist_updated()
Application.ScreenUpdating = False
Range("A10").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.ClearContents
Range("B10:Q10").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.ClearContents
Sheets("Analysis").Select
Range("C5:D5").ClearContents
Range("N6").Select
ActiveCell.FormulaR1C1 = "Yes"
Sheets("Selected Data").Select
Range("C6").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("Watchlist").Select
Range("A10").Select
ActiveSheet.Paste
countermax = Selection.Count
Range("A10").Select
counter = 1
Do Until ActiveCell = ""
sStatus = Format(counter / countermax, "0.0%") & " Complete"
Application.StatusBar = sStatus
Sheets("Analysis").Range("C5") = ActiveCell.Value
Dim array1(16)
Dim myrange As Range
Set myrange = Range(ActiveCell.Offset(0, 1), ActiveCell.Offset(0, 16))
array1(0) = Sheets("Analysis").Range("F5").Value
array1(1) = Sheets("Analysis").Range("C20").Value
array1(2) = Sheets("Analysis").Range("J2").Value
array1(3) = Sheets("Analysis").Range("B8").Value
array1(4) = Sheets("Analysis").Range("J13").Value
array1(5) = Sheets("Analysis").Range("R13").Value
array1(6) = Sheets("Analysis").Range("C21").Value
array1(7) = Sheets("Analysis").Range("B11").Value
array1(8) = Sheets("Analysis").Range("V5").Value
array1(9) = Sheets("Analysis").Range("B12").Value
array1(10) = Sheets("Analysis").Range("J6").Value
array1(11) = Sheets("Analysis").Range("B9").Value
array1(12) = Sheets("Analysis").Range("N20").Value
array1(13) = Sheets("Analysis").Range("H23").Value
array1(14) = Sheets("Analysis").Range("F23").Value
array1(15) = Sheets("Analysis").Range("D23").Value
myrange = array1
ActiveCell.Offset(1, 0).Select
counter = counter + 1
Loop
Application.StatusBar = False
Sheets("Analysis").Select
Range("N6").Select
ActiveCell.FormulaR1C1 = "No"
Sheets("Watchlist").Select
Application.ScreenUpdating = True
Application.StatusBar = False
End Sub
The key to speedy VBA loop is to minimise interaction with the workbook inside the loop.
In your case you won't be able to eliminate interaction entirely, but you can reduce it substantially.
Key steps are:
You can use Manual calculation. (see below)
Create Worksheet and Range objects variables to refer to your sheets and ranges
Create Variant Array's to hold your source data, result data and Analysis results
Once you have a reference to your source data, copy it into a Variant Array. do a For loop over the rows of this array (rather than using ActiveCell)
Create a Results array, sized to the source data rows, by 16 columns wide
On each iteration, copy the source data value onto the Analysis sheet (here's where you can't avoid some workbook interaction)
Force a recalculation of the Analysis sheet with wsAnalysis.Calculate
Copy the results to a variant array in one step. I'd copy the range A1:V23. (Copying too many cells in one step is faster than copying many cells one at a time)
Map the required results into your Results array, into the current row
After the loop, copy the result array to the results range in your workbook (again in one step)
Other notes:
Eliminate all the Select, Selection, ActiveSheet, ActiveCell stuff (as others have mentioned)
Declare all your variables
Be explicit of Lower and Upper bounds in your array declarations
Provide an Error Handler, and CleanUp code to turn on Application properties even when the code errors
After all this, performance will depend on the calculation time of your Analysis worksheet. There may be opportunity for improvement there too, if you would care to share its details
Whilst this won't speed up the entire thing. You can defs save on time by getting rid of the 'select/selection' bits.
For example for that first section replace:
Range("A10").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.ClearContents
with:
Range([A10],[A10].End(xlDown)).ClearContents
Note: the use of [] in this case replaces Range(). Not always healthy to use this shortcut but for your purposes it should be fine.
You should always try and rewrite a code you recorded with this formatting before anything else, it bypasses the clumsiness of the macro recorder and turns it into neat vba code :)
It is not very pretty but it is fast. I am not very good with making Array's faster but this could be an alternative solution.
Sub watchlist_updated()
'***Define your Variables***
Dim wsAnalysis As Excel.Worksheet
Dim wsWatchList As Excel.Worksheet
Dim wsSelectData As Excel.Worksheet
Dim LastRow1 As Long
Dim LastRow2 As Long
Dim LastRow3 As Long
'***Set the objects***
Set wsAnalysis = Sheets("Analysis")
Set wsWatchList = Sheets("Watchlist")
Set wsSelectData = Sheets("Selected Data")
'***Turn off Background***
Application.DisplayAlerts = False
Application.EnableEvents = False
Application.ScreenUpdating = False
'***Finding Last Row - Each Sheet***
LastRow1 = wsSelectData.Range("C" & Rows.Count).End(xlUp).Row
LastRow2 = wsAnalysis.Range("A" & Rows.Count).End(xlUp).Row
LastRow3 = wsWatchList.Range("C" & Rows.Count).End(xlUp).Row
'***Handle any Errors***
On Error GoTo ErrorHandler:
With wsWatchList
.Range(.Cells(10, 1), .Cells(10 + LastRow3, 17)).ClearContents
End With
With wsAnalysis
.Range("C5:D5").ClearContents
.Range("N6").FormulaR1C1 = "Yes"
End With
'***New Copy & Paste Method***
wsWatchList.Range(wsWatchList.Cells(10, 1), wsWatchList.Cells(10 + LastRow1, 1)).Value = _
wsSelectData.Range(wsSelectData.Cells(6, 3), wsSelectData.Cells(6 + LastRow1, 3)).Value
wsAnalysis.Range("C5") = LastRow1 - 5
wsWatchList.Range(wsWatchList.Cells(10, 2), wsWatchList.Cells(LastRow1 + 4, 2)).Value = wsAnalysis.Range("F5").Value
wsWatchList.Range(wsWatchList.Cells(10, 3), wsWatchList.Cells(LastRow1 + 4, 3)).Value = wsAnalysis.Range("C20").Value
wsWatchList.Range(wsWatchList.Cells(10, 4), wsWatchList.Cells(LastRow1 + 4, 4)).Value = wsAnalysis.Range("J2").Value
wsWatchList.Range(wsWatchList.Cells(10, 5), wsWatchList.Cells(LastRow1 + 4, 5)).Value = wsAnalysis.Range("B8").Value
wsWatchList.Range(wsWatchList.Cells(10, 6), wsWatchList.Cells(LastRow1 + 4, 6)).Value = wsAnalysis.Range("J13").Value
wsWatchList.Range(wsWatchList.Cells(10, 7), wsWatchList.Cells(LastRow1 + 4, 7)).Value = wsAnalysis.Range("C21").Value
wsWatchList.Range(wsWatchList.Cells(10, 8), wsWatchList.Cells(LastRow1 + 4, 8)).Value = wsAnalysis.Range("B11").Value
wsWatchList.Range(wsWatchList.Cells(10, 9), wsWatchList.Cells(LastRow1 + 4, 9)).Value = wsAnalysis.Range("V5").Value
wsWatchList.Range(wsWatchList.Cells(10, 10), wsWatchList.Cells(LastRow1 + 4, 10)).Value = wsAnalysis.Range("B12").Value
wsWatchList.Range(wsWatchList.Cells(10, 11), wsWatchList.Cells(LastRow1 + 4, 11)).Value = wsAnalysis.Range("J6").Value
wsWatchList.Range(wsWatchList.Cells(10, 12), wsWatchList.Cells(LastRow1 + 4, 12)).Value = wsAnalysis.Range("B9").Value
wsWatchList.Range(wsWatchList.Cells(10, 13), wsWatchList.Cells(LastRow1 + 4, 13)).Value = wsAnalysis.Range("N20").Value
wsWatchList.Range(wsWatchList.Cells(10, 14), wsWatchList.Cells(LastRow1 + 4, 14)).Value = wsAnalysis.Range("H23").Value
wsWatchList.Range(wsWatchList.Cells(10, 15), wsWatchList.Cells(LastRow1 + 4, 15)).Value = wsAnalysis.Range("F23").Value
wsWatchList.Range(wsWatchList.Cells(10, 16), wsWatchList.Cells(LastRow1 + 4, 16)).Value = wsAnalysis.Range("D23").Value
wsAnalysis.Range("N6").FormulaR1C1 = "No"
wsWatchList.Select
'***Clean Up***
BeforeExit:
Set wsAnalysis = Nothing
Set wsWatchList = Nothing
Set wsSelectData = Nothing
'***Turn on Background***
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
Exit Sub
'***Add in a simple ErrorHandler***
ErrorHandler:
MsgBox "Error"
GoTo BeforeExit
End Sub
Hope this helps!
Related
I prefer to use the function .value, because i dont miss anything, but i cant to paste or the all range
Anyone know how i can do?
Sub AUTO()
Application.ScreenUpdating = False
'sheet that i want paste
PasteData1 = ActiveSheet.Cells(3, 6).Value
DATAAUTO = "L:\ANALISTA_M\Frade\FINANCIAL SERVICES\INSURANCE\Mercado\SUSEP\Planilhas\Auto-Susep.xlsx"
Workbooks.Open (DATAAUTO)
sName = ActiveSheet.Name
'count the number of rows and columns
i = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row - 11
c = ActiveSheet.Cells(6, Columns.Count).End(xlToLeft).Column
'select all range that i want
COPYDATE = ActiveSheet.Range(Cells(6, 1), Cells(i, c)).Value
PASTEAUTO = "L:\ANALISTA_M\Frade\FINANCIAL SERVICES\INSURANCE\Mercado\SUSEP\Auto.xlsm"
Workbooks.Open (PASTEAUTO)
Worksheets(PasteData1).Activate
'the problem is here!!! i need to respect a order the beging my paste
ActiveSheet.Cells(2, 2).Value = COPYDATE
ActiveSheet.Range(Cells(2, 2), Cells(i - 4, c + 1)).Replace What:=".", Replacement:=""
ActiveSheet.Range(Cells(2, 2), Cells(i - 4, c + 1)).Replace What:=",", Replacement:="."
Worksheets("Consolidado").Activate
Workbooks("Auto-Susep.xlsx").Close
Application.ScreenUpdating = True
End Sub
to use the .Value over copy paste you need to have the same size range. You have one cell trying to hold a 2 dimensional array.
So change:
ActiveSheet.Cells(2, 2).Value = COPYDATE
to:
ActiveSheet.Cells(2, 2).Resize(UBound(COPYDATE,1),UBound(COPYDATE,2)).Value = COPYDATE
Also I do not see where you declare any of your variables, that is a bad habit, one should always declare their variables even if they are of a Variant type.
Dim COPYDATE() as Variant
I have a very large data set (600,000 rows) of data structured in the following format:
1) There are around 60 products. One is a Total US number, while the others are for Manufacturers and are labled as KMFs. There are also some labeled as PCKGs(but aren't relevant for this question)
2) Each product is located in 60 different markets
3) Each market has 20 different locations
4) I have 12 metrics for which I need to calculate data in the following manner: Total US number - sum(KMFs) for each metric
I have written vba code for this but it is taking too long to run(around 20 minutes) I need to run similar code on at least 20 worksheets. I have tried various methods such as setting screenUpdating etc. to false. Here is my code. I am new to vba coding so I may have missed obvious things. Please let me know anything is unclear. Please help!
Sub beforeRunningCode()
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
End Sub
Sub returnToOriginal()
Application.ScreenUpdating = screenUpdateState
Application.DisplayStatusBar = statusBarState
Application.Calculation = calcState
Application.EnableEvents = eventsState
ActiveSheet.DisplayPageBreaks = displayPageBreaksState
End Sub
Function LastRowFunc(Sheet) As Long
LastRowFunc = ActiveWorkbook.Worksheets(Sheet).Range("A2", Worksheets(Sheet).Range("A2").End(xlDown)).Rows.Count
End Function
Function LastColFunc(Sheet) As Long
With ActiveSheet
LastColFunc = ActiveWorkbook.Sheets(Sheet).Cells(1, .Columns.Count).End(xlToLeft).Column
End With
End Function
Sub AOCalculate()
Call beforeRunningCode 'Optimize Excel
Dim LastRow As Long
Dim LastCol As Long
Dim Period As String
Dim Sheet As String
Dim Arr(1 To 16)
Dim Count As Integer
Sheet = "Energy_LS_Bottler"
Period = "2016 WAVE 1 - 3 W/E 05/07"
LastRow = LastRowFunc(Sheet) 'Calculate last row for which data exists
LastCol = LastColFunc(Sheet) 'Calculate last column for which data exists
For Each Location In ActiveWorkbook.Sheets("Locations").Range("D7:D28").Value
For Each Market In ActiveWorkbook.Sheets("Markets").Range("A5:A92").Value
Count = Count + 1
Arr(1) = Market
Arr(2) = "AO"
Arr(3) = Location
Arr(4) = Period
With ActiveWorkbook.Sheets(Sheet) 'Filtering for KMF
.AutoFilterMode = False
.Range(Cells(1, 1), Cells(LastRow, LastCol)).AutoFilter
.Range(Cells(1, 1), Cells(LastRow, LastCol)).AutoFilter field:=17, Criteria1:="=KMF"
.Range(Cells(1, 1), Cells(LastRow, LastCol)).AutoFilter field:=1, Criteria1:=Market
.Range(Cells(1, 1), Cells(LastRow, LastCol)).AutoFilter field:=3, Criteria1:=Location
End With
For k = 5 To 16
Arr(k) = Application.WorksheetFunction.Sum(ActiveWorkbook.Sheets(Sheet).Range(Cells(1, k), Cells(LastRow, k)).SpecialCells(xlCellTypeVisible))
Next k
With ActiveWorkbook.Sheets(Sheet) ' filtering for Total US
.AutoFilterMode = False
.Range(Cells(1, 1), Cells(LastRow, LastCol)).AutoFilter
.Range(Cells(1, 1), Cells(LastRow, LastCol)).AutoFilter field:=17, Criteria1:="=Total US"
.Range(Cells(1, 1), Cells(LastRow, LastCol)).AutoFilter field:=1, Criteria1:=Market
.Range(Cells(1, 1), Cells(LastRow, LastCol)).AutoFilter field:=3, Criteria1:=Location
End With
For k = 5 To 16
Arr(k) = -Arr(k) + Application.WorksheetFunction.Sum(ActiveWorkbook.Sheets(Sheet).Range(Cells(1, k), Cells(LastRow, k)).SpecialCells(xlCellTypeVisible))
Next k
For j = 1 To 16
ActiveWorkbook.Sheets(Sheet).Cells(LastRow + Count, j).Value = Arr(j)
Next j
Erase Arr
Next
Next
ActiveWorkbook.Sheets(Sheet).AutoFilterMode = False
Call returnToOriginal
End Sub
[Edit]: Here is a link to a sample data set https://drive.google.com/file/d/0B3MkGa57h6g_WGl2WWlWekd4NU0/view?usp=sharing
I think that this will work (though I haven't had a chance to test it), and should be a lot faster:
Sub AOCalculate()
Call beforeRunningCode 'Optimize Excel
Dim LastRow As Long
Dim LastCol As Long
Dim Period As String
Dim Sheet As String
Dim Arr() '1 To 2000, 1 To 16)
Dim Count As Integer
Sheet = "Energy_LS_Bottler"
Period = "2016 WAVE 1 - 3 W/E 05/07"
LastRow = LastRowFunc(Sheet) 'Calculate last row for which data exists
LastCol = LastColFunc(Sheet) 'Calculate last column for which data exists
'copy all of the relevant cells to local arrays for speed
Dim Locations(), Markets(), data()
Markets = ActiveWorkbook.Sheets("Markets").Range("A5:A92").Value
Locations = ActiveWorkbook.Sheets("Locations").Range("D7:D28").Value
'(pretty sure the following line needs to localize the Cells() to .Cells())
data = ActiveWorkbook.Sheets(Sheet).Range(Cells(1, 1), Cells(LastRow, LastCol)).Value '**'
ReDim Arr(1 To UBound(Markets, 1) * UBound(Locations, 1), 16)
'make an index of pointers into our accumulation array
Dim counts As New Collection
Dim i As Long, l As Long, m As Long
For l = 1 To UBound(Locations, 1)
Location = Locations(l, 1) '**'
For m = 1 To UBound(Markets, 1)
Market = Markets(m, 1) '**'
i = i + 1
counts.Add i, CStr(Location) & "~" & CStr(Market)
'counts.Add NewAccumArray(Location, Market, Period), CStr(Location) & "~" & CStr(Market)
Arr(i, 1) = Market
Arr(i, 2) = "AO"
Arr(i, 3) = Location
Arr(i, 4) = Period
Next
Next
' go through each row and add it to the appropiate count in the array
Dim r As Long
Dim key As String, idx As Long
For r = 1 To UBound(data, 1)
key = CStr(data(r, 3)) & "~" & CStr(data(r, 1))
If data(r, 17) = "KMF" Then
idx = counts(key)
For k = 5 To 16
Arr(idx, k) = Arr(idx, k) - data(r, k)
Next k
Else
If data(r, 17) = "Total US" Then
idx = counts(key)
For k = 5 To 16
Arr(idx, k) = Arr(idx, k) + data(r, k)
Next k
End If
End If
Next r
' output the results
ActiveWorkbook.Sheets(Sheet).Range(Cells(LastRow + 1, 1), Cells(LastRow + Count, 16)).Value = Arr
ActiveWorkbook.Sheets(Sheet).AutoFilterMode = False
Call returnToOriginal
End Sub
Answering the query "What did I mean by this?"
'(pretty sure the following line needs to localize the Cells() to .Cells())
data = ActiveWorkbook.Sheets(Sheet).Range(Cells(1, 1), Cells(LastRow, LastCol)).Value '**'
The use of Cells(..) here is fundamentally unreliable and broken. this is because Cells(..) is really a shortcut for ActiveSheet.Cells(..) and the Active* properties are inherently slow and unreliable because they can change while the code is running. Worse, this code is assuming that ActiveSheet = Energy_LS_Blotter which is far from certain.
The correct way to write this line would be like this:
data = ActiveWorkbook.Sheets(Sheet).Range( _
ActiveWorkbook.Sheets(Sheet).Cells(1, 1), _
ActiveWorkbook.Sheets(Sheet).Cells(LastRow, LastCol) _
).Value
But that is long, ugly and inconvenient. An easier way would be to use either a Sheet variable, or a With:
With ActiveWorkbook.Sheets(Sheet)
data = .Range(.Cells(1, 1), .Cells(LastRow, LastCol)).Value
End With
I have a macro with this loop which take a lot of time :
Dim tempval As String
Dim row As Integer, col As Integer
Application.ScreenUpdating = False
For row = 2 To 500 Step 1
tempval = ""
For col = 7 To 15 Step 1
tempval = tempval & Cells(row, col).Value
Cells(row, col).Value = ""
Next col
Cells(row, 7).Value = tempval
For col = 8 To 16 Step 1
tempval = tempval & Cells(row, col).Value
Cells(row, col).Value = ""
Next col
Cells(row, 8).Value = tempval
Next row
Application.ScreenUpdating = True
Range("LibAnglais2:LibAnglais9").Select
Selection.Delete Shift:=xlToLeft
Range("LibFrancais2:LibFrancais9").Select
Selection.Delete Shift:=xlToLeft
There is code, before, and after this loop.
With this loop, the code takes 3 minutes to end. Without, it takes 30s.
But when I click on the excel windows during the loop (You know when a program run, you click, the window become a white blur screen), my macro finish after I clicked and take approximately 45s...
Do you have an idea why ? And how can fix this to have a faster macro ?
Work with a variant array loaded in bulk directly from the worksheet. Use the Join Function for your concatenation (Chr(124) is the 'pipe' character) and return the processed values back to the worksheet en masse.
Option Explicit
Sub sonic()
Dim r As Long, vTMPs() As Variant, vVALs() As Variant
Application.ScreenUpdating = False
Application.EnableEvents = True
Application.Calculation = xlCalculationManual
With Worksheets("Sheet1")
vTMPs = .Range("G2:P500").Value2
ReDim vVALs(LBound(vTMPs, 1) To UBound(vTMPs, 1), LBound(vTMPs, 2) To 2)
For r = LBound(vVALs, 1) To UBound(vVALs, 1)
vVALs(r, 1) = Join(Array(vTMPs(r, 1), vTMPs(r, 2), vTMPs(r, 3), vTMPs(r, 4), _
vTMPs(r, 5), vTMPs(r, 6), vTMPs(r, 7), vTMPs(r, 8)), Chr(124))
vVALs(r, 2) = Join(Array(vTMPs(r, 2), vTMPs(r, 3), vTMPs(r, 4), vTMPs(r, 5), _
vTMPs(r, 6), vTMPs(r, 7), vTMPs(r, 8), vTMPs(r, 9)), Chr(124))
Next r
.Range("G2:P500").ClearContents
.Range("G2").Resize(UBound(vVALs, 1), UBound(vVALs, 2)) = vVALs
Application.ScreenUpdating = True
'I don't know what the following is supposed to do
.Range("LibAnglais2:LibAnglais9").Delete Shift:=xlToLeft
.Range("LibFrancais2:LibFrancais9").Delete Shift:=xlToLeft
End With
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
Sometimes VBA needs to process messages to be faster. I don't really know why but if some of my macros act up like that I add a DoEvents line in the loop before the Next and it does wonders. It is not recommended for more complex applications. Here you can find a description of it: https://support.office.com/en-us/article/DoEvents-Function-7af41eca-88e0-470d-adaf-0b3d4c2575b0
So your code would be:
DoEvents
Next row
HTH
Ok guys, so, I found the solution.
In fact, I had a other sub in my code :
Private Sub Worksheet_Change(ByVal Target As Range)
And the code pass on this sub each time a cell was modified.
So I put a :
Application.EnableEvents = False
On my code, and it's work !
Thank's for your help !
EDIT : In fact, the problem is not totally solved... I noticed that the code take a lot of time after saving the worksheet or simply after modifiying the code... Do you have a solution ?
I am trying to copy one row to an other workbook (only if there is a match) and i can accomplish that with a simple loop but i would like to use some better and possibly quicker method:
Set wbk = Workbooks.Open(FROM)
Set wskz = wbk.Worksheets("Sheet1")
Set wbi = Workbooks.Open(TO)
Set wski = wbi.Worksheets("Sheet1")
si = 5
Do While wski.Cells(si, 1).Text <> "END" ' loop through the values in column "A" in the "TO" workbook
varver = wski.Cells(si, 1).Text ' data to look up
s = 5
Do While wskz.Cells(s, 1).Text <> "END" ' table where we search for the data in the "FROM" workbook
If wskz.Cells(s, 1).Text = varver Then Exit Do
s = s + 1
Loop
If wskz.Cells(s, 1).Text <> "END" Then
' I am trying this copy method to replace the loop but it throws an error
wskz.Range(Cells(s, 1), Cells(s, 250)).Copy Destination:=wski.Range(Cells(si, 1), Cells(si, 250))
' this is the working loop:
'For i = 1 To 250
' wskz.Cells(s, i) = wski.Cells(si, i)
' i = i + 1
'End If
'Next i
The problem with the new copying method throws an error as it can be seen above.
Thank you in advance for your help!
Try to replace :
wskz.Range(Cells(s, 1), Cells(s, 250)).Copy Destination:=wski.Range(Cells(si, 1), Cells(si, 250))
by
wskz.Range(wskz.Cells(s, 1), wskz.Cells(s, 250)).Copy Destination:=wski.Range(wski.Cells(si, 1), wski.Cells(si, 250))
Or by :
Dim Rng1 As Range, Rng2 As Range
Set Rng1 = wskz.Range(wskz.Cells(s, 1), wskz.Cells(s, 250))
Set Rng2 = wski.Range(wski.Cells(si, 1), wski.Cells(si, 250))
Rng1.Copy Rng2
This should do exactly what you are looking for:
Sub test()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
Dim SourceWS As Worksheet, DestWS As Worksheet
Set SourceWS = Workbooks.Open("FROM").Worksheets("Sheet1")
Set DestWS = Workbooks.Open("TO").Worksheets("Sheet1")
Dim runner As Variant, holder As Range
If IsError(Application.Match("END", DestWS.Range("A5:A" & Rows.Count), 0)) Or IsError(Application.Match("END", SourceWS.Range("A5:A" & Rows.Count), 0)) Then
SourceWS.Parent.Close False
DestWS.Parent.Close False
Exit Sub
End If
Set holder = DestWS.Range("A5:A" & Application.Match("END", DestWS.Range("A5:A" & Rows.Count), 0) + 3)
For Each runner In SourceWS.Range("A5:A" & Application.Match("END", SourceWS.Range("A5:A" & Rows.Count), 0) + 3)
If IsNumeric(Application.Match(runner.Value, holder, 0)) Then runner.EntireRow.Copy DestWS.Rows(Application.Match(runner.Value, holder, 0) + 4)
Next
SourceWS.Parent.Close True
DestWS.Parent.Close True
Application.Calculation = xlCalculationAutomatic
Application.DisplayAlerts = True
End Sub
It is self-explaining to my eye, but if you have any questions, just ask :)
This Error often occures related to Copy-Methods. I also ran into this kind of Error when I had my Sub on Worksheet Level. Try to extract it to a seperate Modul.
Also it seems your references to the Cells are broken. You can find this explained in the docs for Range.Item.
Try this
With wskz
.Range(.Cells(s, 1), .Cells(s, 250)).Copy
End With
Basically, I am trying to pull the data from an Excel file to this worksheet (Auto_Update Sub) and the code is described below:
Sub Auto_Update()
Dim filename As String
Dim r As Integer
Dim i As Double
Dim t As Integer
Dim DPR As Object
Dim new_DPR As Object
Dim well As Object
Dim x As Integer
If IsEmpty(ThisWorkbook.Sheets("SD-28P").Cells(1, 35)) = True Then
ThisWorkbook.Sheets("SD-28P").Cells(1, 35) = Date - 2
End If
Excel.Application.Visible = False
For i = Date - ThisWorkbook.Sheets("SD-28P").Cells(1, 35) To 1 Step -1
filename = "Z:\DPR\DPR_" + Format(Date - i, "yyyymmdd") + ".xls"
Set DPR = Excel.Application.Workbooks.Open(filename)
Set new_DPR = DPR.Worksheets("Daily Production Report")
For x = 247 To 272 Step 1
If Trim(new_DPR.Cells(x, 2).Value) = "SD-01PST" Then t = x
Exit For
For r = t To t + 35 Step 1
Set well = ThisWorkbook.Worksheets(Trim(new_DPR.Cells(r, 2).Value))
f = First_Empty(well, 4)
If new_DPR.Cells(r, 6).Value = Date - i Then
new_DPR.Cells(r, 6).Copy
well.Cells(f, 1).PasteSpecial (xlPasteValues)
new_DPR.Cells(r, 8).Copy
well.Cells(f, 3).PasteSpecial (xlPasteValues)
new_DPR.Cells(r, 10).Copy
well.Cells(f, 4).PasteSpecial (xlPasteValues)
new_DPR.Range(new_DPR.Cells(r, 12), new_DPR.Cells(r, 17)).Copy
well.Range(well.Cells(f, 5), well.Cells(f, 10)).PasteSpecial (xlPasteValues)
new_DPR.Range(new_DPR.Cells(r, 20), new_DPR.Cells(r, 26)).Copy
well.Range(well.Cells(f, 11), well.Cells(f, 17)).PasteSpecial (xlPasteValues)
new_DPR.Range(new_DPR.Cells(r, 28), new_DPR.Cells(r, 30)).Copy
well.Range(well.Cells(f, 18), well.Cells(f, 20)).PasteSpecial (xlPasteValues)
well.Range(well.Cells(f - 1, 2), well.Cells(f - 1, 22)).Copy
well.Range(well.Cells(f, 2), well.Cells(f, 22)).PasteSpecial (xlPasteFormats)
well.Cells(f - 1, 1).Copy
well.Cells(f, 1).PasteSpecial (xlPasteFormulasAndNumberFormats)
End If
Next r
Excel.Application.CutCopyMode = False
DPR.Saved = True
DPR.Close
ThisWorkbook.Application.CutCopyMode = False
Next i
ThisWorkbook.Sheets("SD-28P").Cells(1, 35) = Date
ThisWorkbook.Save
Excel.Application.Visible = True
ThisWorkbook.Sheets(4).Activate
But then, the code returns an error at the line: Next i (Invalid Next Control Variable Reference). I double checked the variable and the syntacx of the For ... Next loop, however, I couldn't not find any possible error. Please kindly help! Thank you very much in advance.
You never close the loop that starts with
For x = 247 To 272 Step 1
If Trim(new_DPR.Cells(x, 2).Value) = "SD-01PST" Then t = x
Exit For
You need a next x somewhere before you can use a next i.