I am trying to accelerate my Excel VB Macro.
I have tried the 5 alternatives below.
But I wonder if I could shorten the execution further.
I found 2 alternatives in User Blogs which I could not get to work.
One alternative is also found in a User Blog but do not understand.
Sub AccelerateMacro()
'
' v1 052817 by eb+mb
' Macro to copy as fast as possible sheet from one workbook into another workbooks
' Declarations for variables are not shown to make code example more legible
' Macro is stored in and run from "DestinationWorkBook.xlsm"
StartTime = Timer
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Alternative = "First"
If Alternative = "First" Then
Workbooks.Open Filename:="SourceWorkBook.xls"
Cells.Select
Selection.Copy
Windows("DestinationWorkBook.xlsm").Activate
Sheets("DestinationSheet").Select
Range("A1").Select
ActiveSheet.Paste
Windows("SourceWorkBook.xls").Activate
ActiveWorkbook.Close
End If
If Alternative = "Second" Then
Workbooks.Open Filename:="SourceWorkBook.xls", ReadOnly:=True
Cells.Select
Selection.Copy
Windows("DestinationWorkBook.xlsm").Activate
Sheets("DestinationSheet").Select
Range("A1").Select
ActiveSheet.Paste
Workbooks("SourceWorkBook.xls").Close SaveChanges:=False
End If
If Alternative = "Third" Then
' I could not get this alternative to work
Workbooks.Open("SourceWorkBook.xls").Worksheets("SourceSheet").Copy
Workbooks.Open("DestinationWorkBook.xlsm").Worksheets("DestinationSheet").Range("A1").PasteSpecial
End If
If Alternative = "Fourth" Then
' I could not get this alternative to work
Workbooks.Open("DestinationWorkBook.xlsm").Worksheets("DestinationSheet").Range("A1") = Workbooks.Open("SourceWorkBook.xls").Worksheets("SourceSheet")
End If
If Alternative = "Fifth" Then
' I don't understand the code in this alternative
Dim wbIn As Workbook
Dim wbOut As Workbook
Dim rSource As Range
Dim rDest As Range
Set wbOut = Application.Workbooks.Open("DestinationWorkBook.xlsm")
Set wbIn = Application.Workbooks.Open("SourceWorkBook.xls")
With wbIn.Sheets("SourceSheet").UsedRange
wbOut.Sheets("DestinationSheet").Range("A1").Resize(.Rows.Count, .Columns.Count) = .Value
End With
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
End Sub
Instead of using UsedRange, find the actual Last Row and Last Column and use that range. UsedRange may not be the range that you think it is :). You may want to see THIS for an explanation.
See this example (UNTESTED)
Sub Sample()
Dim wbIn As Workbook, wbOut As Workbook
Dim rSource As Range
Dim lRow As Long, LCol As Long
Dim LastCol As String
Set wbOut = Workbooks.Open("DestinationWorkBook.xlsm")
Set wbIn = Workbooks.Open("SourceWorkBook.xls")
With wbIn.Sheets("SourceSheet")
'~~> Find Last Row
lRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find Last Column
LCol = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
'~~> Column Number to Column Name
LastCol = Split(Cells(, LCol).Address, "$")(1)
'~~> This is the range you want
Set rSource = .Range("A1:" & LastCol & lRow)
'~~> Get the values across
wbOut.Sheets("DestinationSheet").Range("A1:" & LastCol & lRow).Value = _
rSource.Value
End With
End Sub
Related
I am looking for some help modifying existing code in a worksheet that I had created a while back to copy and paste a range from a row rather than the entire row itself.
The original code, which has worked perfect in the original intended function, it would search column A in the Data worksheet for a specified match. it would then copy that row into a specified worksheet and paste each match as a new row.
What I have been trying to modify the code to do now is perform that same search of column A for either " New, Existing Being Removed, Existing To Remain". When finding one of the 3 options it would then copy the data from columns b:g of that matching row and paste it into the rent worksheet starting at a specified cell. For instance rows marked as Existing to remain would need to star being pasted at cell B3, Existing being removed cell m3, and New cell x3. In total there would not be more than 20 rows from the data sheet that would need to be copied and pasted appropriately.
The code below is the current working code that will search, copy, and paste the entire matching row. Not being extremely proficient with VBA code I didn't want to post the muddled mess that I had made of the original code.
Edit With Photos*
#Toddleson I made the changes you suggested but ended up getting an error with the copyfrom.copy line. It is probably much easier to see what I am trying to accomplish visually. In the Data sheet image link below you will see that row A is where the search occurs. For each match it will copy the values from columns B:G of that row and then paste that into the rent sheet.
If you take a look at the rent image you will see that it is broken into the 3 cooresponding sections. From the match that was found in the cooresponing deisgnation from column A in the data sheet it will then paste the information from columns B:G in the Data to the B:G columns in the Rent sheet.
Data Sheet
Rent
Private Sub CommandButton4_Click()
Dim wb1 As Workbook, wb2 As Workbook
Dim ws1 As Worksheet, ws2 As Worksheet
Dim copyFrom As Range
Dim lRow As Long '<~~ Not Integer. Might give you error in higher versions of excel
Dim strSearch As String
Set wb1 = ThisWorkbook
Set ws1 = wb1.Worksheets("Data")
strSearch = "New"
With ws1
.AutoFilterMode = False
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
With .Range("A1:A" & lRow)
.AutoFilter Field:=1, Criteria1:="=*" & strSearch & "*"
Set copyFrom = .Resize(lRow - 1, 7)
End With
.AutoFilterMode = False
End With
Set ws2 = wb1.Worksheets("Rent")
With ws2
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lRow = .Cells.Find(What:="*", _
After:=Range("p3"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
lRow = 1
End If
copyFrom.Copy ws2.Cells(3, 16).Resize(copyFrom.Rows.Count, copyFrom.Columns.Count)
End With
strSearch = "Existing Being Removed"
With ws1
.AutoFilterMode = False
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
With .Range("A1:A" & lRow)
.AutoFilter Field:=1, Criteria1:="=*" & strSearch & "*"
Set copyFrom = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
End With
.AutoFilterMode = False
End With
Set ws2 = wb1.Worksheets("Rent")
With ws2
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lRow = .Cells.Find(What:="*", _
After:=Range("p19"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
lRow = 1
End If
copyFrom.Copy ws2.Cells(3, 16).Resize(copyFrom.Rows.Count, copyFrom.Columns.Count)
End With
strSearch = "Existing To Remain"
With ws1
.AutoFilterMode = False
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
With .Range("A1:A" & lRow)
.AutoFilter Field:=1, Criteria1:="=*" & strSearch & "*"
Set copyFrom = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
End With
.AutoFilterMode = False
End With
Set ws2 = wb1.Worksheets("Existing To Remain")
With ws2
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lRow = .Cells.Find(What:="*", _
After:=.Range("p35"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
lRow = 1
End If
copyFrom.Copy ws2.Cells(3, 16).Resize(copyFrom.Rows.Count, copyFrom.Columns.Count)
End With
End Sub
I want to loop or find multiple value in another sheets. My code doesn't work even after I do..loop the code.
For i = 1 To lastrowBAU
Worksheets(fname).Range("A1:A" & lastrowsheet).Select
Do Until Cell Is Nothing
Set Cell = Selection.find(What:=ThisWorkbook.Worksheets("BAU").Range("A" & i).Value, After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, _
MatchCase:=False)
If Not Cell Is Nothing Then
Cell.Activate
ActiveCell.Copy
ActiveCell.Insert Shift:=xlShiftDown
ActiveCell.Offset(1, 0).Select
Selection.Replace What:=ThisWorkbook.Worksheets("BAU").Range("A" & i).Value, _
replacement:=ThisWorkbook.Worksheets("BAU").Range("B" & i).Value, _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False
Set Cell = Worksheets(fname).Range("A1:A" & lastrowsheet).FindNext(Cell)
End If
Loop
Next i
You need to set the cell before entering the loop
Set cell = rngSrc.Find(sA, LookIn:=xlFormulas, LookAt:=xlPart, _
After:=rngSrc.Cells(rngSrc.Cells.Count), SearchOrder:=xlByRows, MatchCase:=False)
If Not cell Is Nothing Then
however you also need to avoid an endless loop by checking if the search has returned to the first one found.
Option Explicit
Sub macro1()
Dim ws As Worksheet, wsBAU As Worksheet
Dim cell As Range, rngSrc As Range
Dim fname As String, lastrow As Long, lastrowBAU As Long
Dim i As Long, n As Long, first As String
Dim sA As String, sB As String
fname = "Sheet1"
With ThisWorkbook
Set ws = .Sheets(fname)
Set wsBAU = .Sheets("BAU")
End With
With ws
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rngSrc = .Range("A1:A" & lastrow)
End With
With wsBAU
lastrowBAU = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
With ws
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
Set rngSrc = .Range("A1:A" & lastrow)
End With
' search and replace
Application.ScreenUpdating = False
For i = 1 To lastrowBAU
sA = wsBAU.Cells(i, "A")
sB = wsBAU.Cells(i, "B")
Set cell = rngSrc.Find(sA, LookIn:=xlFormulas, LookAt:=xlPart, _
After:=rngSrc.Cells(rngSrc.Cells.Count), SearchOrder:=xlByRows, MatchCase:=False)
If Not cell Is Nothing Then
first = cell.Address
Do
' insert cell above
cell.Insert xlDown
cell.Offset(-1).Value2 = cell.Value2
cell.Value2 = Replace(cell.Value2, sA, sB)
' expand search range
n = n + 1
Set rngSrc = ws.Range("A1:A" & lastrow + n)
' find next
Set cell = rngSrc.FindNext(cell)
Loop While cell.Address <> first
End If
Next
Application.ScreenUpdating = True
MsgBox n & " replacements", vbInformation
End Sub
I am trying to select a range until the last used row in the sheet. I currently have the following:
Sub Select_Active_Down()
Dim lr As Long
lr = ActiveSheet.UsedRange.Rows.Count
If Cells(ActiveCell.Row, ActiveCell.Column) = Cells(lr, ActiveCell.Column) Then
MsgBox "There isn't any data to select."
Else
Range(Cells(ActiveCell.Row, ActiveCell.Column), Cells(lr, ActiveCell.Column)).Select
Cells(lr, ActiveCell.Column).Activate
End If
End Sub
The issue is that I need to select multiple columns, and this will only select the first column of the active range. How can I modify this to select multiple columns rather than just the first?
What about selection the entire region? This can be done as follows in VBA:
Selection.CurrentRegion.Select
There also is the possibility to select the entire array. For that, just press Ctrl+G, choose Special and see over there.
I would do this slightly different. I would use .Find to find the last row and the last column (using the same logic shown in the link) to construct my range rather than using Selection | Select | ActiveCell | UsedRange | ActiveSheet.
Is this what you are trying?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim LastRow As Long
Dim LastColumn As Long
Dim rng As Range
'~~> Change it to the relevant sheet
Set ws = Sheet1
With ws
'~~> Check if there is data
If Application.WorksheetFunction.CountA(.Cells) = 0 Then
MsgBox "No Data Found"
Exit Sub
End If
'~~> Find last row
LastRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
'~~> Find last column
LastColumn = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Column
'~~> Construct your range
Set rng = .Range(.Cells(1, 1), .Cells(LastRow, LastColumn))
'~~> Work with the range
With rng
MsgBox .Address
'
'~~> Do what you want with the range here
'
End With
End With
End Sub
I have been racking my brain on a bug whereby VBA seems to be using the wrong sheet within a Find function. For purposes such as printing the name of the sheet and values within cells, VBA refers to the sheet I expect. But for the Find function, it reverts to the most recently created sheet and I cannot force a reference to any other sheet. Below is an example that illustrates the problem. The lastRow variable gets assigned based on the Find function from the newly created sheet (three row) whereas the sht variable refers to the five row sheet.
Option Explicit
Dim wb As Workbook
Sub start()
Set wb = ThisWorkbook
Call make5RowSheet
Call make3RowSheet
Call CountRows5RowSheet
End Sub
Sub CountRows5RowSheet()
Dim thing As Variant
Dim sht As Worksheet
Dim lastRow As Long
For Each thing In wb.Worksheets
If LCase(thing.Name) = LCase("five rows") Then Set sht = thing
Next thing
With sht
lastRow = Cells.Find(What:="*", _
After:=.Range("A1"), _ '!!! .range here should refer to five row sheet, but lastRow gets set to 3
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
End With
Debug.Print "sheet name: " & sht.Name 'prints "five rows" as expected
Debug.Print "Cell(3,3) value: " & sht.Cells(3, 1).Value 'blank, as expected
Debug.Print "cell(5,5) value: " & sht.Cells(5, 1).Value 'prints "foo", as expected
Debug.Print "last Row: " & lastRow 'prints 3, which is puzzling
End Sub
Sub make5RowSheet()
Dim sht As Worksheet
Set sht = wb.Worksheets.Add
sht.Name = "five rows"
sht.Cells(5, 1) = "foo"
End Sub
Sub make3RowSheet()
Dim sht As Worksheet
Set sht = wb.Worksheets.Add
sht.Name = "three rows"
sht.Cells(3, 1).Value = "foo"
End Sub
Here
With sht
lastRow = Cells.Find(What:="*", _
Cells is not tied to sht so it refers to the ActiveSheet
With sht
lastRow = .Cells.Find(What:="*", _
should fix things
Your Cells.Find will be executed on whatever sheet is active.
In your case, the last sheet that was created is three rows.
So to avoid this hassle, make sure you Activate the right sheet right before you Find.
sht.Activate ' move from (three rows) to (five rows)
With sht
lastRow = Cells.Find(What:="*", _
After:=.Range("A1"), _ '!!! .range here should refer to five row sheet, but lastRow gets set to 3
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
End With
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I want to make a macro via an if then else function (maybe make use of a loop).
I have two separate files, named "orderregistratie" + "werkorder template".
I want to search in column A of sheets("datablad") in orderregistratie for the value sheets("export datablad").Range("A2") that is in werkorder template.
If this value exists in column A then copy the row of A2 from export datablad and paste it in the row where the value is found.
If it doesn't already exist I want to insert a new row at A2 in orderregistratie and copy the row of A2 from export datablad in the new row.
My VBA knowledge is not really good and I can't write the macro by myself. Is there anyone who can help me write it?
Give this a try. I'll adjust as needed. Just be double check if both workbooks are saved to your desktop.
Option Explicit
Private wkbOrderReg As Workbook, _
wkbOrderWork As Workbook, _
wkb As Workbook
Private wsOBJ As Worksheet, _
ws As Worksheet
Private rngSearch As Range, _
rngRow As Range, _
rng As Range, _
r As Range
Private strSearch As String
Public Sub DarudeSandStorm()
Dim LastRow As Long, _
LastColumn As Long
Dim arr As Variant
With Application.Workbooks
Set wkbOrderReg = .Open(Filename:=strVar("orderregistratie.xlsx"))
Set wkbOrderWork = .Open(Filename:=strVar("werkorder template.xlsx"))
End With
With wkbOrderWork
For Each ws In .Worksheets
Set wsOBJ = ws
If UCase$(wsOBJ.Name) = UCase$("export datablad") Then
With wsOBJ
Set rng = .Range(.Cells(2, 1), .Cells(2, 1))
strSearch = rng.Value
LastColumn = getLAST_COLUMN(wsOBJ)
Set rngRow = .Range(.Cells(2, 1), .Cells(2, LastColumn))
End With
arr = rngRow
Exit For
End If
Next ws
End With
With wkbOrderReg
For Each ws In .Worksheets
Set wsOBJ = ws
If UCase$(wsOBJ.Name) = UCase$("export datablad") Then
With wsOBJ
LastRow = getLAST_ROW(wsOBJ)
Set rngSearch = .Range(.Cells(1, 1), .Cells(LastRow, 1))
End With
For Each r In rngSearch
If UCase$(r.Value) = UCase$(strSearch) Then
r = arr
End If
Next r
End If
Next ws
End With
With Application
For Each wkb In .Workbooks
If Not wkb = .ThisWorkbook Then
With .Workbooks(wkb.Name)
.Save
.Close
End With
End If
Next wkb
End With
End Sub
Private Function getLAST_COLUMN(objWS As Worksheet) As Long
Dim wsDES As Worksheet, _
wkbSUB As Workbook, _
rngCHECK As Range
Set rngCHECK = objWS.Cells.Find(What:="*", _
After:=Range("A1"), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not rngCHECK Is Nothing Then
getLAST_COLUMN = objWS.Cells.Find("*", _
Range("A1"), _
xlFormulas, _
, _
xlByColumns, _
xlPrevious).Column
Else
getLAST_COLUMN = 1
End If
End Function
Private Function getLAST_ROW(objWS As Worksheet) As Long
Dim wsDES As Worksheet, _
wkbSUB As Workbook, _
rngCHECK As Range
Set rngCHECK = objWS.Cells.Find(What:="*", _
After:=Range("A1"), _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
If Not rngCHECK Is Nothing Then
getLAST_ROW = objWS.Cells.Find("*", _
Range("A1"), _
xlFormulas, _
, _
xlByRows, _
xlPrevious).Row
Else
getLAST_ROW = 1
End If
End Function
Private Function strVar(ByRef str As String) As String
strVar = Environ("Userprofile") & "\Desktop\" & str
End Function
#Mischa Urlings for this example i have save both workbooks ("orderregistratie" + "werkorder template") on my desktop so you must change their path on the code.
Option Explicit
Sub test()
Dim WbO As Workbook
Dim WbW As Workbook
Dim i As Long
Dim LRA As Long
Dim RowToCopy As Long
Dim Rowstr As Long
Dim Searchstr As String
Dim Address As Range
Dim Searchrng As Range
Workbooks.Open Filename:="C:\Users\xxxx\Desktop\" & "orderregistratie.xlsx" '<= Open Workbooks (for this example files are stored on desktop
Workbooks.Open Filename:="C:\Users\xxxx\Desktop\" & "werkorder template.xlsx"
Set WbO = Workbooks("orderregistratie.xlsx") '<= Set workbook to variables
Set WbW = Workbooks("werkorder template.xlsx")
LRA = WbW.Worksheets("export datablad").Range("A" & Rows.Count).End(xlUp).Row '<= Find Lastrow
For i = 2 To LRA '<= Loop column A (Workbook:werkorder template)
Searchstr = WbW.Worksheets("export datablad").Range("A" & i).Value '<= Set what to search for
Rowstr = i '<= Searchstr row
Set Searchrng = WbO.Worksheets("datablad").Columns("A") '<= Set where to search for
Set Address = Searchrng.Find(What:=Searchstr, LookAt:=xlWhole) '<= Result of the search
If Address Is Nothing Then
'If what we search for not found
WbO.Worksheets("datablad").Rows("2").Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
WbW.Worksheets("export datablad").Rows(Rowstr).EntireRow.Copy
WbO.Worksheets("datablad").Rows(2).PasteSpecial Paste:=xlPasteValues
Else
'If what we search for found
RowToCopy = Address.Row '<= Where we find the Searchstr
WbW.Worksheets("export datablad").Rows(i).EntireRow.Copy
WbO.Worksheets("datablad").Rows(RowToCopy).PasteSpecial Paste:=xlPasteValues
End If
Next i
With WbO
.Save
.Close '<= Close open workbooks
End with
With WbW
.Save
.Close '<= Close open workbooks
End with
End Sub