I have a code that loop through excel files in a folder and copy the value and paste them to a new workbook.
The problem occur when I have files that only have a single value in the cell. It return an error stating
copy area and paste area aren't the same size
Below is my code:
Sub MergeDataFromWorkbooks()
'DECLARE AND SET VARIABLES
Dim wbk As Workbook
Dim wbk1 As Workbook
Set wbk1 = ThisWorkbook
Dim Filename As String
Dim Path As String
Path = "C:\Users\Desktop\merge all to one\" 'CHANGE PATH ACCORDING TO FOLDER DIRECTORY LEAVING \ AT THE END
Filename = Dir(Path & "*.xlsx")
'--------------------------------------------
'OPEN EXCEL FILES
Do While Len(Filename) > 0 'IF NEXT FILE EXISTS THEN
Set wbk = Workbooks.Open(Path & Filename)
wbk.Activate
Range("A2").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Windows("Book1.xlsm").Activate
Application.DisplayAlerts = False
Dim lr As Double
lr = wbk1.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
Sheets(1).Select
Cells(lr + 1, 1).Select
ActiveSheet.Paste
wbk.Close True
Filename = Dir
Loop
MsgBox "All the files are copied and pasted in Book1."
End Sub
First of all some thoughts to improve your coding style
You should avoid using Selection, Select and Activate because this is a bad practice and slows down your code a lot. You can do all actions without using them. In most cases you should never use them (there are a very little number of special cases).
Don't use eg. Range or Cells without specifying a worksheet. Otherwise Excel tries to guess which worksheet you mean and it will probably fail doing this. Guessing is not knowing, therefore always tell Excel which worksheet you mean like Worksheets(1).Range or Worksheets("SheetName").Range.
Use descriptive variable names. Names like wbk and wbk1 are not very descriptive and later you don't know what wbk1 was and mess things up. Instead use something like wbDestination and wbSource everybody knows what that means now.
Also it might be a good practice to declare the variables close to their first use, especially when code gets a bit longer.
Always use Worksheets instead of Sheets if possible. Sheets also contains charts not only workbooks but in most cases you just want the Worksheets. You say it doesn't matter? Well it does. Sheets(1).Range will throw an error if the first sheet is a chart. We can avoid that.
Now lets start tidy up …
Instead of activate, select 3 times and copy
wbk.Activate
Range("A2").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
We can just copy without any ativate or select which is a lot faster and has the same effect:
With wbSource.Worksheets(1).Range("A2")
'copy without select
.Resize(.End(xlDown).Row - .Row + 1, .End(xlToRight).Column - .Column + 1).Copy
End With
When we close the source workbook
wbSource.Close SaveChanges:=False
we don't need to save the changes because we didn't change anything. This is more secure and a lot faster.
So we end up with
Option Explicit
Sub MergeDataFromWorkbooks()
Dim wbDestination As Workbook
Set wbDestination = ThisWorkbook
Dim Path As String
Path = "C:\Temp\" 'make sure it ends with \
Dim Filename As String
Filename = Dir(Path & "*.xlsx")
Do While Len(Filename) > 0 'while file exists
Dim wbSource As Workbook
Set wbSource = Workbooks.Open(Path & Filename)
With wbSource.Worksheets(1).Range("A2")
'copy without select
.Resize(.End(xlDown).Row - .Row + 1, .End(xlToRight).Column - .Column + 1).Copy
End With
Dim lRow As Double
lRow = wbDestination.Worksheets(1).Cells(Rows.Count, 1).End(xlUp).Row 'find next empty row
wbDestination.Worksheets(1).Cells(lRow + 1, 1).PasteSpecial Paste:=xlPasteAll 'paste all
wbSource.Close SaveChanges:=False 'we don't need to save changes we didn't change anything just copied
Filename = Dir 'next file
Loop
MsgBox "All the files are copied and pasted in Book1."
End Sub
Alternative way to determine the last used cell (column and row) in the source file
This avoids errors when row 2 is the last used row.
With wbSource.Worksheets(1).Range("A2")
.Resize(.Parent.Cells(.Parent.Rows.Count, .Column).End(xlUp).Row - .Row + 1, .Parent.Cells(.Row, .Parent.Columns.Count).End(xlToLeft).Column - .Column + 1).Copy
End With
Explanation:
.Parent.Cells(.Parent.Rows.Count, .Column).End(xlUp).Row
finds the last used row in column A by starting from the very last cell in Excel and going up (like pressing ctrl + up).
I don't see why your code is thrown a Copy Area and Paste area aren't the same size error. Unless there are merged cells.
Select and Active are generally used to show the user something. You can and should not use them unless absolutely necessary. I recommend watching: Excel VBA Introduction Part 5 - Selecting Cells (Range, Cells, Activecell, End, Offset)
Dim Source As Range
Application.DisplayAlerts = False
Do While Len(Filename) > 0 'IF NEXT FILE EXISTS THEN
With Workbooks.Open(Path & Filename)
Set Source = .Range(.Range("A" & .Rows.Count).End(xlUp), .Cells(1,
.Columns.Count).End(xlToLeft))
End With
Source.Copy Workbooks("Book1.xlsm").Range("A" & .Rows.Count).End(xlUp)
.Close False
Filename = Dir
Loop
Related
I'm looking for assistance regarding how to write a VBA command that allows me to copy a range of cells from different workbooks onto one master sheet. Let me explain further.
Everyday I receive a new excel document from my clients named based on the date it was uploaded ie. September 18, 2018 file would be called A20180918.
Once i've received a week's worth of excel files (A20180918-A20180921), I then have to copy certain information from the original uploaded file to a master tracking sheet.
So, my hurdle is such that each time I record my actions into a macro, the code includes the file name which then creates a subscript error when it's run on the next day's file.
So here's an example below of the code I have this far:
Sub CopyRange()
CopyRange Macro
'This is the line of the code that's causing problems given it's a specified workbook name
'and when I try to open tomorrow's workbook i'll run into the subscript error.
Windows("A20180914.xls").Activate
Range("A2:B2").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Windows("Master Sheet.xlsm").Activate
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
Thank you!
Here's two solutions. One to scan an Directory for files, and the other to select files. I see they've both been suggested in the comments already. :p
Sub Test()
' If you want to scan an "unprocessed files" directory
'Call ScanDir("C:\Test\")
' If you want to select files to process
Call SelectFiles
End Sub
Private Sub ScanDir(ByVal DirPath As String)
Dim oCurFile As String
oCurFile = Dir(DirPath)
Do While oCurFile <> ""
' Add the work to the file here
' Filename: DirPath & oCurFile
oCurFile = Dir()
Loop
End Sub
Private Sub SelectFiles()
Dim oFileDialog As FileDialog
Set oFileDialog = Application.FileDialog(msoFileDialogFilePicker)
oFileDialog.AllowMultiSelect = True
If oFileDialog.Show = -1 Then
Dim oFile As Variant
For Each oFile In oFileDialog.SelectedItems
' Add the work to the file here
' Filename: oFile
Next
End If
End Sub
By the looks of it you have all the workbooks open when you run the code - there are ways to have the code open each workbook in a certain folder, or ask the user to select them.
While writing this other answers have given the code for selecting files from folders.
Each workbook in the Excel Application is held in a collection of workbooks. The good thing about collections is you can step through them without know the specifics before you get there.
The code below will print the name of each workbook you have open into the immediate window. Note - these are in the same instance of Excel. If you open Excel a second time then any workbooks in that application will be in a different collection.
You don't really need the Application but I left it in to make things a bit clearer.
Sub Test()
Dim bk As Workbook
For Each bk In Application.Workbooks
Debug.Print bk.Name
Next bk
End Sub
This is the full code - note that nothing is Selected.
Sub Test()
Dim bk As Workbook
Dim Master As Workbook
Dim LastCell As Range
Set Master = Workbooks("Master Sheet.xlsm")
For Each bk In Application.Workbooks
'Checks the middle part of the file name - should be a number.
'Better ways to check the file name are available.
If IsNumeric(Mid(bk.Name, 3, 8)) Then
'Copy date from Sheet1. It's assumed each row in
'column B is populated and figures out the last cell from there.
With bk.Worksheets("Sheet1")
Set LastCell = .Cells(.Rows.Count, 2).End(xlUp)
.Range("A1", LastCell).Copy
End With
'Pastes the results to Sheet1 in the Master workbook.
'The last cell containing data in column A is found and
'then offset by 1 row.
With Master.Worksheets("Sheet1")
.Range("A" & .Rows.Count).End(xlUp).Offset(1).PasteSpecial Paste:=xlPasteValues
End With
End If
Next bk
End Sub
This will do it, you just need to supply the sheet name and ranges where noted:
Sub copyRange()
'File system variables
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim myDir As String
Dim Mask As String
'Workbook variables
Dim wb As Workbook
Dim sh As Worksheet
myDir = "C:\Users\Guest\Desktop" 'Insert the path where your incoming files are stored.
Mask = "*.xl??" 'This makes it so it only looks at Excel files.
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.getFolder(myDir)
For Each objFile In objFolder.Files
If LCase(objFile.Name) Like LCase(Mask) Then
Set wb = Workbooks.Open(myDir & "\" & objFile.Name, , True) 'This is set to open in read only, to avoid issues with the file already being open
'The ranges you are copying/pasting are vague, supply the sheet names and ranges below
'Get Copy range with dynamic number of rows.
With wb.Sheets("Sheet1").Range("A2:B2") '<---- Specify Sheet/Range
Set copyRange = .Resize(.End(xlDown).Row, 2)
End With
'Get next available row in paste range.
With ThisWorkbook.Sheets("Sheet1").Range("G:H") '<---- Specify Sheet/Range
Set pasteRange = .Cells(.Rows.Count, 1).End(xlUp).Offset(1, 0)
End With
copyRange.Copy pasteRange
wb.Close False
End If
Next objFile
End Sub
I'm currently looking for a code to improve my Dashboard. Actually, I need to know how to simplify and make my code more efficient. It's working but It's longer and longer day after day because of the amount of data/traffic at certain hours on the company network.
I know it's not correct to use "Select" but I didn't find an answer to my problem.
Situation:
I created my layout on SAP, and I export them (default file name is
'Export.xls')
I go to my Main Excel File (called 'Dashboard') and I
run the code from the WS concerned by the layout exported
The username need to be captured in case I'm out of office, and someone else need to run the code.
When Data are imported from SAP Export to my main file, it closes the SAP "Export" file
This is my current code:
Sub PasteSAP()
'
' Pull Data From SAP Export - Excel File
'
Dim UserName As String
UserName = Environ("username")
'Clear "PasteSAP" sheet in case the next one will have less data
Range("A:O").Select
Selection.ClearContents
'Open SAP Excel file (the export)
Workbooks.Open "C:\Users\" & UserName & "\Desktop\export.XLSX"
Windows("export.XLSX").Activate
'Copy data of the SAP Excel file
Range("A:O").Select
Selection.Copy
'Go back to the main file and paste in the active worksheet
Windows("Dashboard - 2017.xlsm").Activate
Range("A:O").Select
ActiveSheet.Paste
'Close SAP Excel file
Windows("export.XLSX").Activate
Application.DisplayAlerts = False
ActiveWindow.Close
'Change Format
Range("A:A").Select 'specify the range which suits your purpose
With Selection
Selection.NumberFormat = "General"
.Value = .Value
End With
Dim wks As Worksheet
Dim lastRow As Long
Dim r As Long
Set wks = ActiveSheet
lastRow = ActiveSheet.Cells.SpecialCells(xlLastCell).Row
For r = 2 To lastRow
If wks.Cells(r, 1) <> "" Then
wks.Cells(r, 7).NumberFormat = "General"
wks.Cells(r, 9).Style = "Currency"
End If
Next r
Range("A1").Select
End Sub
Since you are copying the whole columns you do not Need to erase them beforehand.
Usually you would not want to jump between windows since it takes lots of time.
Also turning off ScreenUpdating could speed things up.
Try the following code:
...
Application.ScreenUpdating = False
dim wb_export as Workbook
dim ws_export_from as Worksheet, ws_export_to as Worksheet
Set wb_export = Workbooks.open("...\Export.xls")
Set ws_export_from = wb_export.Worksheets("your worksheet")
Set ws_export_to = Worksheets("Destination worksheet")
ws_export_from.range("A:O").Copy Destination := ws_export_to.Range("A:O")
wb_export.close false
set wb_export = Nothing
set ws_export_from = Nothing
set ws_export_to = Nothing
Application.ScreenUpdating = True
This should run a lot faster.
I have the below code.
Very simply it asks the user to select multiple excel workbooks and then will copy and paste data from those workbooks to the current work book.
1.
I would like to add the functionality, whereby instead of the user selecting the excel workbooks. The excel workbooks will be selected in that their names are listed on the current excel sheet.
For example - Select excel workbooks in specified folder whose names are listed in A1:A5.
I would like to perform automatic processing on the data before it is copied into the current work book.
For example if workbook name = 100.xlsx then multiply selection by 15.
See my current code
Sub SUM_BalanceSheet()
Application.ScreenUpdating = False
'FileNames is array of file names, file is for loop, wb is for the open file within loop
'PasteSheet is the sheet where we'll paste all this information
'lastCol will find the last column of PasteSheet, where we want to paste our values
Dim FileNames
Dim file
Dim wb As Workbook
Dim PasteSheet As Worksheet
Dim lastCol As Long
Set PasteSheet = ActiveSheet
lastCol = PasteSheet.Cells(1, Columns.Count).End(xlToLeft).Column
'Build the array of FileNames to pull data from
FileNames = Application.GetOpenFilename(filefilter:="Excel Files (*.xlsx), *.xlsx", MultiSelect:=True)
'If user clicks cancel, exit sub rather than throw an error
If Not IsArray(FileNames) Then Exit Sub
'Loop through selected files, put file name in row 1, paste P18:P22 as values
'below each file's filename. Paste in successive columns
For Each file In FileNames
Set wb = Workbooks.Open(file, UpdateLinks:=0)
PasteSheet.Cells(1, lastCol + 1) = wb.Name
wb.Sheets("Page 1").Range("L14:L98").Copy
PasteSheet.Cells(2, lastCol + 1).PasteSpecial Paste:=xlPasteValues
wb.Close SaveChanges:=False
lastCol = lastCol + 1
Next
'If it was a blank sheet then data will start pasting in column B, and we don't
'want a blank column A, so delete it if it's blank
If Cells(1, 1) = "" Then Cells(1, 1).EntireColumn.Delete shift:=xlLeft
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
This is a frame that needs fine-tuning, but you can get the idea:
Dim i&, wbName$
Dim rng As Excel.Range
Dim wb, wb1 As Excel.Workbook
Set wb = Application.ThisWorkbook
Set rng = wb.Sheets("Sheet1").Range("A1")
For i = 0 To 14
wbName = CStr(rng.Offset(i, 0).Value)
On Error Resume Next 'Disable error handling. We will check whether wb is nothing later
wb1 = Application.Workbooks.Open(wbName, False)
On Error GoTo ErrorHandler
If Not IsNothing(wb1) Then
'Copy-paste here
If wb1.Name = "100" Then 'any condition(s)
'Multiply, divide, or whatever
End If
End If
Next
ErrorHandler:
MsgBox "Error " & Err.Description
'Add additional error handling
Try not to use ActiveSheet and ActiveWorkbook without absolute need. Use ThisWorkbook, dedicated Workbook object, and named sheet Workbook.Sheets("Name") or Workbook.Sheets(index) instead.
Alternatively instead of disabling error checking you can do it and fail if a file is missing.
I have been trying to sum multiple workbooks with multiple worksheets with the same format. So far, I was following this post. Although I have taken a look at this and also this links trying to get a good and short idea, the first one was working nice, so I followed.
It was going pretty well so far with the post I mentioned first. However, there are one (small) problem that I could not get the answer anywhere. How can I make the code to work without having to select the files? I have they all listed in a column in the workbook called "Main", and they are all in the same folder, however, I don't know how to get them automatically, without having to manually select.
For instance, I wanted to take the files (and their address) names in, for example, Sheet(1), Range("A1:A100") in the Workbook "Main".
Can anyone give me a hand? This is the code I'm using:
Sub Results()
Dim WS_Count As Integer 'not being used
Dim FileNameXls, f
Dim wb As Workbook, i As Integer
'locate where are the Templates and how many sheets they have
Range("Template").Select
ncol = ActiveCell.Column
Selection.End(xlToRight).Select
lastcolumn = ActiveCell.Column
numSheets = lastcolumn - ncol
'Name of the First Template
Business = Cells(2, ncol)
Windows("StressTestPlatform.xlsm").Activate
'THIS IS WHERE I'M ASKED TO SELECT THE FILES
FileNameXls = Application.GetOpenFilename(filefilter:="Excel Files, *.xl*", MultiSelect:=True)
If Not IsArray(FileNameXls) Then Exit Sub
Application.ScreenUpdating = False
For Each f In FileNameXls
Set wb = Workbooks.Open(f)
For i = 3 To numSheets
wb.Worksheets(i).Range("C5:H93").Copy
'The Range must be changed accordingly to the template being used
Workbooks("Main.xlsm").Sheets("Results").Range("C5:H93").PasteSpecial Paste:=xlPasteValues, Operation:=xlAdd, SkipBlanks:=True, Transpose:=False
Next i
Application.CutCopyMode = False
wb.Close SaveChanges:=False
Next f
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
If you have the full file paths stored in a range somewhere, why not just loop through that range and open each file?
Dim TemplateRange as Range
Dim r as Range
Set TemplateRange = ThisWorkbook.Sheets(1).Range("A1:A100")
'^^ Change this to wherever your list of files is stored.
'You can eliminate the GetOpenFilename dialog entirely
'FileNameXls = Application.GetOpenFilename(filefilter:="Excel Files, *.xl*", MultiSelect:=True)
'Instead, just loop through the template range one by one and get
'each filename and proceed with the rest of your code as before
For Each r In TemplateRange
FileNameXls = r.Value2
Set wb = Workbooks.Open(FileNameXls)
'
'The rest of your code as before
'
Next r
If your template range has the workbook names, but not the full file path, you'll have to do a little extra work and get the directory of your Main workbook (assuming all the other files are in the same directory), and then append the workbook name to that.
In a previous posting I asked about how to highlight a cell range that began with a certain keyphrase and ended when the next cell was blank. I would like to gain a better understanding of how to create a loop that performs this on multiple Excel files. Any help would be much appreciated. For reference, the code I am referring to is as follows:
Dim wk As Worksheet
Set wk = ActiveSheet
FirstRowColA = Application.WorksheetFunction.Match("keyphrase", wk.[A:A])
LastRowColA = Cells(wk.Rows.Count, "A").End(xlUp).Row
wk.Range("A" & FirstRowColA & ":A" & LastRowColA).Copy
Worksheets("Sheet2").Paste
In addition, I was curious about how to handle creating a "Sheet 2" if one does not exist already in the active workbook. Do I need to use something like Set WS = Sheets.Add and have Excel look at Worksheets(Sheets.Add).Paste?
I have also noticed that this code does not necessarily find what I am telling it to find, but this is an issue I should be able to resolve. For example, putting the phrase "Name" in the Match() function returns the text of a cell in column A containing a different word.
Let say u have excel files in the some folder
this code opens each workbook in the folder and searches specific string if found .copy and paste the required data.
Sub LoopThroughFiles()
Dim StrFile As String
Dim wk As Worksheet
StrFile = Dir("C:\Personal\Excel Report\*.xlsx")
Do While Len(StrFile) > 0
Workbooks.Open ("C:\Personal\Excel Report\" & StrFile)
Set wk = ActiveSheet
Set firstrowcola = activesheet.Range("A:A").Find("taskname") ' - search taskname in 1st row
If firstrowcola Is Nothing Then GoTo here:
LastRowColA = Cells(wk.Rows.Count, "A").End(xlUp).Row
wk.Range(firstrowcola.address & ":" & firstrowcola.offset(lastrowcola,0).address)).Copy
Set ws = Sheets.Add
ws.Range("A1").Select
ActiveSheet.Paste
here:
ActiveWorkbook.Close True
Loop
End Sub