Merge Columns from multiple Workbooks in Excel with VBA

I had an opportunity where I had several workbooks (5 to be exact) that are updated on a regular basis, daily in some cases. Each workbook has similar information but in different locations. For my usage I desired to combine the applicable columns into a single set of columns in one Excel file. A merged document will allow me to look in one location rather than five, and find all the results.

I searched and searched and wasn’t able to find anyone else who had solved my particular problem. I set out to learn a little Visual Basic For Applications (VBA) so that I could accomplish the task.

The first thing to do when looking to solve a problem for yourself is to look at This Chart. This Script shaves 15 minutes off a task that between me and the other people that use it is done almost daily. It took me less than 11 hours to get the code to work so I think I came out ahead. Hopefully this code will save you that much time in getting your solution working.

You’ll notice in the code that I reference two Rectangles. The first is used to start the VBA Script  and show when the information was last updated. The second lists the opened Workbooks and the date they were last updated. These Shapes and the associated code is optional.

Sub Merge()
'This text write doesn't actually show up due to the speed the function runs 
'I placed it here to try and get it to load, it might for you
ActiveSheet.Shapes("Rectangle 1").TextFrame.Characters.Text = "Merge Documents" & vbNewLine & "NO ERROR CHECKING!" & vbNewLine & vbNewLine & "RUNNING"

Dim MyArray(5, 3) As String, x As Integer
Dim SourceRange As Range
Dim WorkBk As Workbook
Dim SummarySheet As Worksheet
'Defined so I can reference anytime I want
Set SummarySheet = ThisWorkbook.Sheets("Sheet1")
Dim Heading As String
'I define this here so I can use LEN() on it later to customize the format
Heading = vbNewLine & "Dates of Change Sheets as of the last run" With SummarySheet.Shapes("Rectangle 2").TextFrame
 .Characters.Font.Color = vbWhite 'reset color
 .Characters.Text = Heading & vbNewLine 'write to box
End With

 'Set SummarySheet = ActiveWorkbook
 Dim DestRange As Range

'turn off things that slow down functions
Dim screenUpdateState As Long
screenUpdateState = Application.ScreenUpdating 'so you can reset at the end
Application.ScreenUpdating = False

'For my case, I have all the Workbooks in the same place
'If you have different locations you can hard link in the array
'If you needed to look at other than the first Sheet or
'in multiple sheets you could add another dimension with that value
Dim FolderPath As String
FolderPath = ActiveWorkbook.Path
    ChDrive FolderPath
    ChDir FolderPath
'I have hard coded each Workbook and the columns I desire
MyArray(0, 0) = "REPORT.xls"    'filename
MyArray(0, 1) = "A"             'First Desired Column
MyArray(0, 2) = "B"             'Second Desired Column

MyArray(1, 0) = "IMPACTS.xlsx"
MyArray(1, 1) = "C"
MyArray(1, 2) = "A"

MyArray(2, 0) = "hat.xlsx"
MyArray(2, 1) = "H"
MyArray(2, 2) = "A"

MyArray(3, 0) = "Report2.xlsx"
MyArray(3, 1) = "A"
MyArray(3, 2) = "C"

MyArray(4, 0) = "REPORT3.xlsx"
MyArray(4, 1) = "D"
MyArray(4, 2) = "A"

x = UBound(MyArray, 1) - LBound(MyArray, 1) - 1 'count MyArray for use later on

'Clear Sheet1 data
'I used .Delete here for a while but it moved my buttons and I didn't like that
'This is important in case the length of data changes
Columns(1).EntireColumn.ClearContents 
Columns(2).EntireColumn.ClearContents 

'define row_counter variable to keep track of where to paste the new data
Dim row_counter As Long
row_counter = 1

'write headers in Sheet1
Range("A" & row_counter) = "Document"
Range("B" & row_counter) = "Change Paper"
row_counter = row_counter + 1

'For to loop through MyArray
Dim i As Integer
For i = 0 To x
Dim WB As Workbook
'Write Workbooks name and the date it was last updated
With SummarySheet.Shapes("Rectangle 2").TextFrame
 .Characters.Text = .Characters.Text & vbNewLine & MyArray(i, 0) & " " & "  Saved: " & FileDateTime(MyArray(i, 0))
End With

'Opens the Workbooks in order, if it is already open there will be an error
Set WorkBk = Workbooks.Open(MyArray(i, 0))
         Dim LastRow As Long
'Count rows with data in selected column
LastRow = WorkBk.Worksheets(1).Cells(Rows.Count, MyArray(i, 1)).End(xlUp).Row
    Set SourceRange = WorkBk.Worksheets(1).Range(MyArray(i, 1) & "2:" & MyArray(i, 1) & LastRow)
    
    'Set the destination range to start at first column and be the same size as the source range.
        Set DestRange = SummarySheet.Range("A" & row_counter)
               Set DestRange = DestRange.Resize(SourceRange.Rows.Count, _
                 SourceRange.Columns.Count)
                      ' Copy over the values from the source to the destination.
        DestRange.Value = SourceRange.Value
        'repeat for second column
        Set SourceRange = WorkBk.Worksheets(1).Range(MyArray(i, 2) & "2:" & MyArray(i, 2) & LastRow)
          Set DestRange = SummarySheet.Range("B" & row_counter)
        Set DestRange = DestRange.Resize(SourceRange.Rows.Count, _
           SourceRange.Columns.Count)
           
        ' Copy over the values from the source to the destination.
        DestRange.Value = SourceRange.Value
        
WorkBk.Close savechanges:=False 'Close Workbook
row_counter = row_counter + LastRow - 1 'keep track of where to paste new data
Next i

'turn back on things that slow down functions
Application.ScreenUpdating = screenUpdateState

'Call AutoFit on the destination sheet so that all data is readable.
    SummarySheet.Columns.AutoFit
    'Applies sorting filter
    Worksheets("Sheet1").Range("A:B").AutoFilter _
 field:=1, _
 VisibleDropDown:=True
Dim dt As String
dt = Format(CStr(Now), "General Date") 'timestamp
'Write Last Run date to button
ActiveSheet.Shapes("Rectangle 1").TextFrame.Characters.Text = "Merge Documents" & vbNewLine & "NO ERROR CHECKING!" & vbNewLine & vbNewLine & "Last Run on " & dt
'Style Header for first row
With SummarySheet.Shapes("Rectangle 2").TextFrame
 .Characters(1, Len(Heading)).Font.Color = vbBlue
 End With

End Sub

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.