I\'m new to macros in Excel and I need to make a macro that get data from multiple sheets in a selected workbook.
So far I have this code to select a file and get da
You could try this: https://msdn.microsoft.com/en-us/library/office/gg549168(v=office.14).aspx I don't know if it helps.
To do this with Excel Automation, first define the following function, which gets the last used cell in a worksheet, using the technique outlined here:
Function LastUsedCell(wks As Excel.Worksheet) As Excel.Range
With wks
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
Set LastUsedCell = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False)
End If
End With
End Function
and this helper function, to determine where to start copying the data from each worksheet:
Function GetNextRowStart(wks As Excel.Worksheet) As Excel.Range
Dim lastCell As Excel.Range
Dim nextRow As Integer
nextRow = 1
Set lastCell = LastUsedCell(wks)
If Not lastCell Is Nothing Then nextRow = lastCell.Row + 1
Set GetNextRowStart = wks.Cells(nextRow, 1)
End Function
Then you can use the following code:
Dim outputWorkbook As Excel.Workbook
Dim outputWorksheet As Excel.Worksheet
Dim filepath As Variant
Set outputWorkbook = Workbooks.Open("D:\Zev\Clients\stackoverflow\outputMultipleWokrbooksWithADO\output.xlsx")
Set outputWorksheet = outputWorkbook.Sheets("Sheet1")
For Each filepath In Application.GetOpenFilename(FileFilter:="Excel Files (*.xl*), *.xl*", MultiSelect:=True)
Dim wkbk As Excel.Workbook
Dim wks As Excel.Worksheet
Set wkbk = Workbooks.Open(filepath, , True)
For Each wks In wkbk.Sheets
Dim sourceRange As Excel.Range
Dim outputRange As Excel.Range
With wks
Set sourceRange = .Range(.Cells(1, 1), LastUsedCell(wks))
End With
Set outputRange = GetNextRowStart(outputWorksheet)
sourceRange.Copy outputRange
Next
Next
outputWorksheet.Columns.AutoFit
The previous approach uses Excel Automation -- open the workbook, get a hold of the sheet, manipulate ranges on the source and output sheets. Data can be copied as is or transformed in some way, during the move.
You can also use ADODB to read the Excel sheets as if the workbook was a database and the worksheets were its tables; and then issue an INSERT INTO
statement to copy the original records into the output workbook. It offers the following benefits:
Value
property of a Range
object, which returns a two-dimensional array. This can easily be assigned / pasted to anything which expects such an array, including the Value
property itself.However, it suffers from the following limitations:
INSERT INTO
requires that the source and the destination have the same number of fields, with the same data types. (In this case, the SQL can be modified to insert to a different set or order of destination fields, and to use different source fields).INSERT INTO
..xls
input and output, no .xlsx
.'Sheet1$'
, there will be 'Sheet1$'FilterDatabase
(or Sheet1$_
when using the Jet provider).Add a reference (Tools -> References ...) to Microsoft ActiveX Data Objects. (Choose the latest version; it's usually 6.1).
The output workbook and worksheet should exist. Also, both the input and output workbooks should be closed while running this code.
Dim filepath As Variant
Dim outputFilePath As String
Dim outputSheetName As String
'To which file and sheet within the file should the output go?
outputFilePath = "c:\path\to\ouput.xls"
outputSheetName = "Sheet1"
For Each filepath In Application.GetOpenFilename(FileFilter:="Excel Files (*.xl*), *.xl*", MultiSelect:=True)
Dim conn As New ADODB.Connection
Dim schema As ADODB.Recordset
Dim sql As String
Dim sheetname As Variant
With conn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=""" & filepath & """;" & _
"Extended Properties=""Excel 12.0;HDR=No"""
'To use the old Microsoft Jet provider:
'.Provider = "Microsoft.Jet.OLEDB.4.0"
'.ConnectionString = "Data Source=""" & filepath & """;" & _
' "Extended Properties=""Excel 8.0;HDR=No"""
.Open
End With
Set schema = conn.OpenSchema(adSchemaTables)
For Each sheetname In schema.GetRows(, , "TABLE_NAME") 'returns a 2D array of one column
'This appends the data into an existing worksheet
sql = _
"INSERT INTO [" & outputSheetName & "$] " & _
"IN """ & outputFilePath & """ ""Excel 12.0;"" " & _
"SELECT * " & _
"FROM [" & sheetname & "]"
'To create a new worksheet, use SELECT..INTO:
'sql = _
' "SELECT * " & _
' "INTO [" & outputSheetName & "$] " & _
' "IN """ & outputFilePath & """ ""Excel 12.0;"" " & _
' "FROM [" & sheetname & "]"
conn.Execute sql
Next
Next
Dim wbk As Workbook
Set wbk = Workbooks.Open(outputFilePath)
wbk.Worksheets(outputSheetName).Coluns.AutoFit
An alternate approach is to read the data with ADODB into a recordset and then paste it into the output workbook using the CopyFromRecordset method:
Dim filepath As Variant
Dim outputFilePath As String
Dim outputSheetName As String
Dim sql As String
Dim wbk As Workbook, wks As Worksheet
Dim rng As Excel.Range
Dim sheetname As Variant
'To which file and sheet within the file should the output go?
outputFilePath = "c:\path\to\ouput.xlsx"
outputSheetName = "Sheet1"
For Each filepath In Application.GetOpenFilename(FileFilter:="Excel Files (*.xl*), *.xl*", MultiSelect:=True)
Set schema = conn.OpenSchema(adSchemaTables)
For Each sheetname In schema.GetRows(, , "TABLE_NAME") 'returns a 2D array of one column
sql = sql & _
"UNION ALL SELECT F1 " & _
"FROM [" & sheetname & "]" & _
"IN """ & filepath & """ ""Excel 12.0;"""
Next
Next
sql = Mid(sql, 5) 'Gets rid of the UNION ALL from the first SQL
Dim conn As New ADODB.Connection
Dim rs As ADODB.Recordset
With conn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=""" & filepath & """;" & _
"Extended Properties=""Excel 12.0;HDR=No"""
.Open
Set rs = .Execute(sql)
Set wbk = Workbooks.Open(outputFilePath, , True)
Set wks = wbk.Sheets(outputSheetName)
wks.Cells(2, 1).CopyFromRecordset rs
wks.Columns.AutoFill
.Close
End With
Jet SQL:
ADO:
See also this answer, which is doing something similar.