How do I avoid using select/activate in my macro (to help speed it up)?
The macro goes through each row on a worksheet; if the QTY is greater than zero (in column C), then it calls another macro to open a specific workbook (workbook name in column A), makes some changes and then closes that workbook.
Sub Update_All_Workbooks()
Dim LastRow As Long
Dim DataRange As Range
Dim WB As Workbook
Dim WS As Worksheet
Set WB = ActiveWorkbook
Set WS = ActiveSheet
LastRow = Cells(Rows.Count, "A").End(xlUp).Row
Set DataRange = Sheets("TestA").Range("A3:A" & LastRow)
Application.ScreenUpdating = False
Application.DisplayAlerts = False
WB.Sheets("TestA").Activate
Range("C3").Select
For Each Row In DataRange
If ActiveCell > 0 Then
Call Open_Update_Close_WB
WB.Sheets("TestA").Activate
ActiveCell.Offset(1, 0).Select
Else
ActiveCell.Offset(1, 0).Select
End If
Next Row
WS.Activate
End Sub
Its quite a change in perspective to move from using select to using references but in the long run, code is much better when using references.
I hop the code below is useful to you.
Option Explicit
Sub Update_All_Workbooks()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim myWB As Workbook
Set myWB = ActiveWorkbook
' We set myWS on the basis of the unqualified Cell method used in th original code
Dim myWS As Worksheet
Set myWS = myWB.ActiveSheet
Dim LastRow As Long
LastRow = myWS.Cells(Rows.Count, "A").End(xlUp).Row
' Pull the filenames into a VBA array
' So we don't keep having to refder to a Worksheet
' The transpose method is used to convert the pseudo 2D array
' to a correct 1D array
Dim myWbNames As Variant
Set myWbNames = myWB.Application.WorksheetFunction.Transpose(myWS.Range("A3:A" & LastRow).Value)
' Similar to above, you can extract the QTY values in
' column C to a VBA array
Dim myQTY As Variant
Set myQTY = myWB.Application.WorksheetFunction.Transpose(myWS.Range("C3:C" & LastRow).Value)
' Because we are processing two arrays (col a and col c)
' its easier to use a standard for loop with an index than a for each loop
Dim myIndex As Variant
For myIndex = LBound(myWbNames) To UBound(myWbNames)
If myQTY(myIndex) > 0 Then
Open_Update_Close_WB myWbNames(myIndex)
End If
Next
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
' Underscores have significance in Method names as they are used in
' interface and event declarations
' Therefore it is good practise to get used to NOT using underscores
' for Method names that do not involve an interface
Public Sub OpenUpdateCloseWB(ByVal ipWbName As String)
End Sub
Related
I don't really understand VBA, all I can do is paste a code I find that does what I need (thanks to all of your excellent answers to previous questions.) Sometimes I'm able to make simple modifications.
I found a code that's perfect for what I need to do (copy one worksheet to all other sheets), but I need to exclude the first 7 sheets.
The one I'm using already excludes the source sheet, but I haven't been able to figure out how to expand that to include more.
This is the code I'm using:
Dim aWshExcluded As Variant, vWshExc As Variant
aWshExcluded = Array("Exclude(1)", "Exclude(2)")
Dim WshSrc As Worksheet
Dim WshTrg As Worksheet
Rem Set Source Worksheet
Set WshSrc = ThisWorkbook.Worksheets(2)
Application.ScreenUpdating = 0
Rem Process All Worksheets
For Each WshTrg In WshSrc.Parent.Worksheets
Rem Exclude Worksheet Source
If WshTrg.Name <> WshSrc.Name Then
Rem Validate Worksheet vs Exclusion List
For Each vWshExc In aWshExcluded
If WshTrg.Name = vWshExc Then GoTo NEXT_WshTrg
Next
Rem Process Worksheet Target
With WshTrg.Cells
WshSrc.Cells.Copy
.PasteSpecial Paste:=xlPasteAll 'Everything is pasted.
Application.CutCopyMode = False
Application.Goto .Cells(1), 1
End With: End If:
NEXT_WshTrg:
Next
Application.Goto Worksheets("Main").Cells(1), 1
Application.ScreenUpdating = 1
End Sub
Thank you!
Process Worksheets (With Exclusion)
A safer way would be to write the names of the exclusion worksheets to an array and use Application.Match in the loop to find a worksheet name in the array.
Using indexes instead of names is not recommended, e.g. if you move the 2nd worksheet to another position, the code will fail i.e. it will copy the wrong sheet. Also, if you move the main worksheet to a position greater than the ExclusionCount it will be overwritten.
A Quick Fix
Option Explicit
Sub CopySheet()
Const ExclusionsCount As Long = 7
Dim Wbk As Workbook
Dim WshSrc As Worksheet
Dim WshTrg As Worksheet
Dim n As Long
Set Wbk = ThisWorkbook
Set WshSrc = Wbk.Worksheets(2)
Application.ScreenUpdating = False ' or 0
For Each WshTrg In Wbk.Worksheets
n = n + 1
If n > ExclusionsCount Then
WshSrc.Cells.Copy
With WshTrg
.Cells.PasteSpecial Paste:=xlPasteAll
Application.Goto .Cells(1), 1
End With
End If
Next
Application.Goto Worksheets("Main").Cells(1), 1
Application.ScreenUpdating = True ' or "- 1" in VBA, not "1", although it works.
End Sub
EDIT
The Safer Way
Sub CopySheetWithExclusions()
Const ExclusionsList As String = "Main,Source," _
& "Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday"
Dim Wbk As Workbook
Dim WshSrc As Worksheet
Dim WshTrg As Worksheet
Dim Exclusions() As String
Dim n As Long
Set Wbk = ThisWorkbook
Set WshSrc = Wbk.Worksheets("Source")
Exclusions = Split(ExclusionsList, ",")
Application.ScreenUpdating = False
For Each WshTrg In Wbk.Worksheets
If IsError(Application.Match(WshTrg.Name, Exclusions, 0)) Then
WshSrc.Cells.Copy
With WshTrg
.Cells.PasteSpecial Paste:=xlPasteAll
Application.Goto .Cells(1), 1
End With
End If
Next
Application.Goto Worksheets("Main").Cells(1), 1
Application.ScreenUpdating = True
End Sub
I found a macro that subtracts the values in one cell in a workbook from another cell in a workbook to output the result in a final third workbook. It exists as such
Sub Sample()
Dim wb1 As Workbook, wb2 As Workbook, wb3 As Workbook
Dim lngDiff As Long
On Error GoTo Err
Application.ScreenUpdating = False
Set wb1 = ActiveWorkbook
Set wb2 = Workbooks.Open("C:\FirstDataFile.xlsx")
Set wb3 = Workbooks.Open("C:\SecondDataFile.xlsx")
lngDiff = wb2.Sheets("Sheet1").Range("A1").Value - _
wb3.Sheets("Sheet1").Range("A1").Value
wb1.Sheets("Sheet1").Range("A1").Value = lngDiff
wb3.Close savechanges:=False
wb2.Close savechanges:=False
Application.ScreenUpdating = True
Exit Sub
Err:
MsgBox Err.Description
End Sub
Is there anyway to modify this code that it can do this for multiple lines at once.
For example. get it to subtract wb2.Sheets("Sheet1").Range("A1").Value - _
wb3.Sheets("Sheet1").Range("A1").Value and output that result into wb1.Sheets("Sheet1").Range("A1").Value and then do the same for A2, A3 and so on so forth until about A:120000? I would also like to be able to get this done on multiples sheets on the two books that I am drawing info from. How would this be done?
Thanks!
I suggest to use a loop through a list of worksheet names, and outsource the subtraction to subroutine InAllValuesOfColumnA that loops through all rows of each sheet as shown below. I further recommend to use meaningful variable names instead of numbered variables (which is a bad practice and easily gets mixed up).
Option Explicit
Public Sub ExampleSample()
Dim wbResult As Workbook, wbData As Workbook, wbSubtract As Workbook
Dim lngDiff As Long
On Error GoTo Err
Application.ScreenUpdating = False
Set wbResult = ActiveWorkbook
Set wbData = Workbooks.Open("C:\FirstDataFile.xlsx")
Set wbSubtract = Workbooks.Open("C:\SecondDataFile.xlsx")
Dim WorksheetList() As Variant
WorksheetList = Array("Sheet1", "Sheet2") 'add the worksheet names here
Dim WsName As Variant
For Each WsName In WorksheetList
InAllValuesOfColumnA OfWorksheet:=wbData.Worksheets(WsName), SubtractWorksheet:=wbSubtract.Worksheets(WsName), WriteToWorksheet:=wbResult.Worksheets(WsName)
Next WsName
wbData.Close SaveChanges:=False
wbSubtract.Close SaveChanges:=False
Application.ScreenUpdating = True
Exit Sub
Err:
MsgBox Err.Description
End Sub
Private Sub InAllValuesOfColumnA(ByVal OfWorksheet As Worksheet, ByVal SubtractWorksheet As Worksheet, ByVal WriteToWorksheet As Worksheet)
Dim LastRow As Long
LastRow = OfWorksheet.Cells(OfWorksheet.Rows.Count, "A").End(xlUp).Row
Dim iRow As Long
For iRow = 1 To LastRow 'run from first to last row and subtract
WriteToWorksheet.Cells(iRow, "A").Value = CLng(OfWorksheet.Cells(iRow, "A").Value - SubtractWorksheet.Cells(iRow, "A").Value)
Next iRow
End Sub
An even faster way would be to read/write the data into arrays before/after calculation:
Private Sub InAllValuesOfColumnA(ByVal OfWorksheet As Worksheet, ByVal SubtractWorksheet As Worksheet, ByVal WriteToWorksheet As Worksheet)
Dim LastRow As Long
LastRow = OfWorksheet.Cells(OfWorksheet.Rows.Count, "A").End(xlUp).Row
'read all into array
Dim DataColumn() As Variant
DataColumn = OfWorksheet.Range("A1:A" & LastRow).Value
Dim SubtractColumn() As Variant
SubtractColumn = SubtractWorksheet.Range("A1:A" & LastRow).Value
Dim ResultColumn() As Variant
ResultColumn = WriteToWorksheet.Range("A1:A" & LastRow).Value
Dim iRow As Long
For iRow = LBound(ResultColumn) To UBound(ResultColumn) 'run from first to last row and subtract
ResultColumn(iRow) = CLng(DataColumn(iRow) - SubtractColumn(iRow))
Next iRow
WriteToWorksheet.Range("A1:A" & LastRow).Value = ResultColumn
End Sub
The VBA I created is creating the separate sheets. However (in this example there is three sheets being created), after the first sheet is created and the VBA runs back through the code, it copies the first sheet that was created, rather than the master sheet, which already has the data needed for the second and third tab filtered out so there is no data in the final two tabs. Do you know what could be causing this? Code below:
Option Explicit
Sub InvoiceSeperator()
' Declare objects
Dim sourceSheet As Worksheet
Dim targetSheet As Worksheet
Dim sourceRange As Range
Dim targetRange As Range
Dim SplitOrderNum As Range
Dim OrderData As Range
Dim sourceCell As Range
Dim allOrders As Range
Dim invoicenumbers As Range
' Declare other variables
Dim sourceSheetName As String
Dim sourceRangeName As String
Dim targetSheetName As String
Dim targetRangeName As String
Dim lastSheetHidden As Boolean
' <<< Customize this >>>
sourceSheetName = "Invoices"
targetSheetName = "SumToLineItem"
sourceRangeName = "SplitOrderNum"
targetRangeName = "OrderData"
' Initialize the source sheet
Set targetSheet = ThisWorkbook.Sheets("SumToLineItem")
Set allOrders = targetSheet.Range("B:B")
allOrders.Select
Selection.Copy
Sheets.Add After:=ActiveSheet
ActiveSheet.Paste
Sheets(2).Select
Sheets(2).Name = "Invoices"
Application.CutCopyMode = False
ActiveSheet.Range("A:A").RemoveDuplicates Columns:=1, Header:=xlYes
Set sourceSheet = ThisWorkbook.Sheets("Invoices")
sourceSheet.Range("A2").Select
sourceSheet.Range(Selection, Selection.End(xlDown)).Select
ActiveWorkbook.Names.Add Name:="SplitOrderNum", RefersToR1C1:= _
"=Invoices!R2C1:R4C1"
' Initialize the range (Add full qualifier to the current workbook, sheet and range)
Set sourceRange = sourceSheet.Range("SplitOrderNum")
' Get if last sheet is visible in current workbook
lastSheetHidden = Not ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count).Visible
ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count).Visible = True
For Each sourceCell In sourceRange
' Copy the source worksheet
targetSheet.Copy After:=Worksheets(ThisWorkbook.Sheets.Count)
' Rename the new worksheet
Sheets(ThisWorkbook.Sheets.Count).Name = sourceCell.Value
' Reference to the added worksheet
Set targetSheet = ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
With targetSheet.Range(targetRangeName)
.AutoFilter Field:=2, Criteria1:="<>" & sourceCell.Value,.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
' Check this next line if this should point to the orderdata range too (?)
targetSheet.AutoFilter.ShowAllData
Next sourceCell
' Return the last sheet visible state
If lastSheetHidden = False Then
ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count).Visible = Not lastSheetHidden
End If
End Sub
I have an Excel workbook that has the potential for a large number of sheets to be added in or removed. Each of these will have a standard suffix, let's call this ".A"
What I would like is a macro that for each worksheet with this suffix, copies all data from a selected range on each worksheet (say:A1:X50), copies it to a new consolidated worksheet, moves to the next line on the consolidated sheet and repeats for each subsequent worksheet. So far, I have this... but it doesn't work.
Sub compile()
SelectSheets ".A", ThisWorkbook
'Some other bits and pieces here
End Sub
Sub SelectSheets(sht As String, Optional wbk As Workbook)
Dim wks As Worksheet
Dim ArrWks() As String
Dim i As Long
If wbk Is Nothing Then Set wbk = ActiveWorkbook
ReDim ArrWks(0 To Worksheets.Count - 1)
For Each wks In Worksheets
If InStr(1, wks.Name, sht) > 0 Then
ArrWks(i) = wks.Name
i = i + 1
End If
Next wks
ReDim Preserve ArrWks(i - 1)
Sheets(ArrWks).Select
For Each ws In Sheets(ArrWks)
ws.Range("D36:CT46").Copy
Worksheets("Summary").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial (xlPasteValues)
Next ws
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
There are other changes i would make to this code but the basics are as follows; where you loop the array containing the worksheet names and do your copying.
Note:
1) You are picking up any worksheet name with .A in, not just those with it as a suffix.
2) You might also want some error handling in case no sheets are found as then your array will end up throwing an out of bounds error.
3) Your first paste will be to row 2 if you don't test if last row = 1.
Looping of array:
For ws = LBound(ArrWks) To UBound(ArrWks)
A test for the suffix might better be
If Right$(wks.Name, 2) = ".A" Then
Code:
Option Explicit
Sub compile()
SelectSheets ".A", ThisWorkbook
'Some other bits and pieces here
End Sub
Sub SelectSheets(sht As String, Optional wbk As Workbook)
Dim wks As Worksheet
Dim ArrWks() As String
Dim i As Long
If wbk Is Nothing Then Set wbk = ActiveWorkbook
ReDim ArrWks(0 To Worksheets.Count - 1)
For Each wks In Worksheets
If InStr(1, wks.Name, sht) > 0 Then
ArrWks(i) = wks.Name
i = i + 1
End If
Next wks
ReDim Preserve ArrWks(i - 1)
Dim ws As Long
For ws = LBound(ArrWks) To UBound(ArrWks)
Worksheets(ArrWks(ws)).Range("D36:CT46").Copy
Worksheets("Summary").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).PasteSpecial (xlPasteValues)
Next ws
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
I'm currently doing VBA project which need to copy from a workbook to another, which the WBookPst is the workbook I firstly open (use) meanwhile WBookCopy is the workbook where I open based on the links where I got by listing all ".xslt" format in a File into my Sheet1 of my first workbook. Here is my code :
Sub SortFiles()
'Set up your variables and turn off screen updating.
'Dim iCounter As Integer
Application.ScreenUpdating = False
'Sort the rows based on the data in column C
Columns("A:C").Sort key1:=Range("C2"), _
order1:=xlDescending, Header:=xlYes
Application.ScreenUpdating = True
Dim WBookCopy As Workbook
Dim WBookPst As Workbook
Dim filePath As String
Dim sheetName As String
Dim sheetCopy As Worksheet
Dim sheetPate As Worksheet
Dim rngCopy As Range
Dim rngPst As Range
filePath = Range("B2").Value
Set WBookCopy = Workbooks.Open(filePath)
Columns(30).Insert
For i = 1 To Sheets.count
Cells(i, 30) = Sheets(i).Name
Next i
sheetName = Range("AD1").Value
Set sheetCopy = WBookCopy.Worksheets(sheetName)
Set rngCopy = sheetCopy.Range("A:AA").Copy
Set WBookPst = ThisWorkbook
Set sheetPaste = WBookPst.Worksheets("Sheet1").Activate
Set rngCopy = sheetPaste.Range("A:AA").Select
ActiveSheet.Paste
End Sub
At Set rngCopy = sheetCopy.Range("A:AA").Copy there's error "Objects required".
What does that mean?
By the way, is how I copy and paste the data between sheets correct?
The issue is that rngCopy is of type range and you can't set it equal to a method (copy). Remove the .Copy and you should be fine. You also don't need to set the worksheet out range to a variable. You could just do one line that says WBookCopy.SheetName.Range("A:AA").Copyand then another line to paste.
As #Wyatt mentioned - your copy\paste syntax is incorrect
Here are 2 ways to do it:
Worksheets("Sheet1").Range("A:AA").Copy
Worksheets("Sheet2").Range("A1").PasteSpecial xlPasteAll
or
Worksheets("Sheet1").Range("A:AA").Copy Destination:=Worksheets("Sheet2").Range("A1")