Excel·VBA合并工作簿

1,合并文件夹下所有工作簿

适用将所有工作簿中所有工作表复制到1个新建工作簿中,不修改数据,原本一共有多少个工作表,合并后就有多少个工作表
如果存在同名工作表,复制后工作表名称会自动添加序号,如Sheet1 (2)

Sub 合并文件夹下所有工作簿()
    '文件夹下所有工作簿wb所有工作表ws合并为一个新工作簿(但不含子文件夹),不修改数据
    Dim write_wb As Workbook, wb As Workbook, sht As Worksheet, file_path$, file_name$
    file_path = "E:\测试\拆分表\"  '待合并工作簿所在的文件夹
    file_name = Dir(file_path & "*.xlsx")
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    Set write_wb = Workbooks.Add    '新建工作簿,合并文件
    Do While file_name <> ""
        Set wb = Workbooks.Open(file_path & file_name)
        For Each sht In wb.Worksheets
            sht.Copy After:=write_wb.Sheets(write_wb.Sheets.Count)
        Next
        wb.Close (False)
        file_name = Dir  '下一个文件名
    Loop
    '保存文件
    save_file = file_path & "合并表.xlsx"
    write_wb.SaveAs filename:=save_file
    write_wb.Close (False)
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
End Sub

1.1,合并且建立超链接目录

Sub 合并文件夹下所有工作簿并建立目录()
    '文件夹下所有工作簿wb所有工作表ws合并为一个新工作簿(但不含子文件夹),不修改数据,并建立目录超链接
    Dim write_wb As Workbook, wb As Workbook, list_ws As Worksheet, sht As Worksheet
    Dim fso As Object, file_path$, file_name$, full_name$, newname$, w&
    file_path = "E:\测试\拆分表\"  '待合并工作簿所在的文件夹
    file_name = Dir(file_path & "*.xlsx")
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    Set write_wb = Workbooks.Add    '新建工作簿,合并文件
    Set list_ws = write_wb.Worksheets(1): list_ws.Name = "目录"
    list_ws.Cells(1, 1) = "目录(原工作簿名-工作表名)": list_ws.Cells(1, 2) = "超链接": w = 1
    Set fso = CreateObject("Scripting.FileSystemObject")
    Do While file_name <> ""
        Set wb = Workbooks.Open(file_path & file_name)
        For Each sht In wb.Worksheets
            sht.Copy After:=write_wb.Sheets(write_wb.Sheets.Count)
            full_name = fso.GetBaseName(file_name) & "-" & sht.Name  '原工作簿名-工作表名
            'write_wb.Sheets(write_wb.Sheets.Count).Name = full_name  '可对复制的ws重命名
            w = w + 1: list_ws.Cells(w, 1) = full_name: newname = write_wb.Sheets(write_wb.Sheets.Count).Name
            list_ws.Hyperlinks.Add anchor:=list_ws.Cells(w, 2), Address:="", SubAddress:="'" & newname & "'!a1", TextToDisplay:=newname
        Next
        wb.Close (False)
        file_name = Dir  '下一个文件名
    Loop
    '保存文件
    list_ws.Columns(1).AutoFit  '列宽自适应
    save_file = file_path & "合并表.xlsx"
    write_wb.SaveAs filename:=save_file
    write_wb.Close (False)
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
End Sub
举例

合并《Excel·VBA按列拆分工作表、工作簿》,sub2拆分后的工作表
在这里插入图片描述
并且每个工作簿中的工作表复制1个副本(1个地名表1个Sheet1表),这样就有5个工作簿各含2个工作表
工作簿合并且建立超链接目录结果
在这里插入图片描述

2,合并工作簿中所有工作表

对工作簿中相同格式的工作表进行合并,汇总所有工作表,保存在工作簿最前

2.1,纵向合并

Sub 合并工作簿中所有工作表_纵向()
    '当前工作簿wb所有工作表ws合并保存至新建工作表(插入最前),但之前ws不修改(工作表格式相同)
    Dim wb, ws, title_row, end_row, copy_title, i
'--------------------参数填写:title_row,数字,第1行为1向下递增;end_row,数字
    title_row = 1  '表头行数,仅复制1次;如果为0,则表示没有表头或表头每次都复制
    end_row = 0    '表尾行数,不参与合并
    Set wb = Application.ActiveWorkbook  '当前工作簿即为待合并工作簿
    Set ws = wb.Worksheets.Add(before:=Sheets(1))  '最前添加新sheet,即为合并工作表
    ws.Name = "合并表"
    If title_row > 0 Then copy_title = True Else copy_title = False  '是否复制表头
    If title_row < 0 Then Debug.Print "title_row参数错误,必须为>=0的整数": Exit Sub
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    '遍历,复制表体
    For i = 1 To Worksheets.count:
        If Worksheets(i).Name <> ws.Name Then
            If copy_title = True Then  '复制表头,仅执行1次
                Worksheets(i).Rows(1 & ":" & title_row).Copy ws.Range("A1")
                copy_title = False
            End If
            '首行为空,会导致后续数据被覆盖
            If WorksheetFunction.CountA(ws.Rows(1)) = 0 Then ws.Rows(1).Delete
            write_row = ws.UsedRange.Rows.count + 1  '合并工作表的第一个空行写入
            sheet_row = Worksheets(i).UsedRange.Rows.count
            Worksheets(i).Rows(title_row + 1 & ":" & sheet_row - end_row).Copy ws.Range("A" & write_row)
        End If
    Next
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
End Sub
举例

合并《Excel·VBA按列拆分工作表、工作簿》,sub1拆分后的工作表
在这里插入图片描述
在这里插入图片描述
合并参数:title_row = 1,end_row = 0
在这里插入图片描述
在这里插入图片描述

2.2,横向合并

Sub 合并工作簿中所有工作表_横向()
    '当前工作簿wb所有工作表ws合并保存至新建工作表(插入最前),但之前ws不修改(工作表格式相同)
    Dim ws As Worksheet, sht As Worksheet, write_col&
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    With ActiveWorkbook
        Set ws = .Worksheets.Add(before:=Sheets(1))  '最前添加新sheet,即为合并工作表
        ws.Name = "合并表"
        For Each sht In .Worksheets
            If sht.Name <> ws.Name Then
                '首列为空时,会导致后续数据被覆盖
                If WorksheetFunction.CountA(ws.Columns(1)) = 0 Then ws.Columns(1).Delete
                write_col = ws.UsedRange.Columns.Count + 1
                sht.UsedRange.Copy ws.Cells(1, write_col)
            End If
        Next
    End With
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
End Sub
举例

合并前
在这里插入图片描述
合并后
在这里插入图片描述

3,合并文件夹下所有工作簿中所有工作表

对相同格式的工作簿进行合并,汇总所有工作表,保存为单独工作簿

Sub 合并文件夹下所有工作簿中所有工作表()
    '文件夹下所有工作簿wb所有工作表ws合并保存至新建工作表(但不含子文件夹),但不修改原数据(工作表格式相同)
    Dim wb, ws, title_row, end_row, copy_title, file_path, file_name, i
'--------------------参数填写:title_row,数字,第1行为1向下递增;end_row,数字;file_path,合并文件夹
    title_row = 1  '表头行数,仅复制1次;如果为0,则表示没有表头或表头每次都复制
    end_row = 0    '表尾行数,不参与合并
    file_path = "E:\测试\拆分表\"  '待合并工作簿所在的文件夹
    file_name = Dir(file_path & "*.xlsx") 
    If title_row > 0 Then copy_title = True Else copy_title = False  '是否复制表头
    If title_row < 0 Then Debug.Print "title_row参数错误,必须为>=0的整数": Exit Sub
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    Workbooks.Add    '新建工作表
    Set ws = ActiveSheet
    ws.Name = "合并表"
    Do While file_name <> ""
        Set wb = Workbooks.Open(file_path & file_name)
        For i = 1 To Worksheets.count:
            If copy_title = True Then  '复制表头,仅执行1次
                wb.Worksheets(i).Rows(1 & ":" & title_row).Copy ws.Range("A1")
                copy_title = False
            End If
            '首行为空,会导致后续数据被覆盖
            If WorksheetFunction.CountA(ws.Rows(1)) = 0 Then ws.Rows(1).Delete
            write_row = ws.UsedRange.Rows.count + 1  '合并工作表的第一个空行写入
            sheet_row = wb.Worksheets(i).UsedRange.Rows.count
            wb.Worksheets(i).Rows(title_row + 1 & ":" & sheet_row - end_row).Copy ws.Range("A" & write_row)
        Next
        wb.Close (False)
        file_name = Dir  '下一个文件名
    Loop
    '保存文件
    ws.Parent.SaveAs filename:=file_path & "合并表.xlsx"
    ws.Parent.Close (False)
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
End Sub

举例

合并《Excel·VBA按列拆分工作表》,sub2拆分后的工作表
在这里插入图片描述
合并参数:title_row = 0,end_row = 0
在这里插入图片描述
在这里插入图片描述

3.1,合并且显示原工作簿名称、原工作表名称

2022.8.27更新,应评论建议
增加在A列显示原工作簿名称,B列显示原工作表名称

Sub 合并文件夹下所有工作簿中所有工作表1()
    '文件夹下所有工作簿wb所有工作表ws合并保存至新建工作表(但不含子文件夹),但不修改原数据(工作表格式相同)
    Dim write_ws As Worksheet, wb As Workbook, sht As Worksheet, fso As Object
    Dim title_row&, end_row&, write_row&, sht_row&, sht_col&, copy_row&, file_path$, file_name$
'--------------------参数填写:title_row,数字,第1行为1向下递增;end_row,数字;file_path,合并文件夹
    title_row = 1  '表头行数,仅复制1次;如果为0,则表示没有表头或表头每次都复制
    end_row = 0    '表尾行数,不参与合并
    file_path = "E:\测试\拆分表\"  '待合并工作簿所在的文件夹
    file_name = Dir(file_path & "*.xlsx")
    If title_row < 0 Then Debug.Print "title_row参数错误,必须为>=0的整数": Exit Sub
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    Set fso = CreateObject("Scripting.FileSystemObject")
    Do While file_name <> ""
        Set wb = Workbooks.Open(file_path & file_name)
        For Each sht In wb.Worksheets
            If WorksheetFunction.CountA(sht.UsedRange.Cells) <> 0 Then  '非空工作表
                If write_ws Is Nothing Then
                    sht.Copy: Set write_ws = ActiveSheet  '整体复制工作表
                    write_ws.Name = "合并表": write_ws.Columns("a:b").Insert '插入列
                    write_ws.[a1].Resize(1, 2) = Array("原工作簿名称", "原工作表名称")
                    write_row = write_ws.UsedRange.Rows.Count
                    write_ws.[a2].Resize(write_row - title_row, 2) = Array(fso.GetBaseName(file_name), sht.Name)
                    If end_row > 0 Then  '删除表尾行
                        write_ws.Cells(write_row, 1).Offset(-end_row + 1, 0).Resize(end_row, 1).EntireRow.Delete
                    End If
                Else
                    write_row = write_ws.UsedRange.Rows.Count + 1  '合并工作表的第一个空行写入
                    sht_row = sht.UsedRange.Rows.Count: sht_col = sht.UsedRange.Columns.Count
                    copy_row = sht_row - title_row - end_row  '复制行数
                    sht.Cells(title_row + 1, "a").Resize(copy_row, sht_col).Copy write_ws.Cells(write_row, "c")
                    write_ws.Cells(write_row, "a").Resize(copy_row, 2) = Array(fso.GetBaseName(file_name), sht.Name)
                End If
            End If
        Next
        wb.Close (False)  '关闭工作簿
        file_name = Dir   '下一个文件名
    Loop
    '保存文件
    write_ws.Parent.SaveAs filename:=file_path & "合并表.xlsx"
    write_ws.Parent.Close (False)
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
End Sub

4,合并文件夹下所有工作簿中同名工作表

对工作簿按工作表名称进行合并,汇总所有同名工作表,保存为单独工作簿

Sub 合并文件夹下所有工作簿中同名工作表()
    '文件夹下所有工作簿wb所有工作表ws按名称合并保存至新建工作表(但不含子文件夹),但不修改原数据(工作表格式相同)
    Dim dict As Object, sht As Worksheet, file_path$, file_name$, title_row, end_row, save_file$
'--------------------参数填写:title_row,数字,第1行为1向下递增;end_row,数字;file_path,合并文件夹
    title_row = 1  '表头行数,不参与合并
    end_row = 0    '表尾行数,不参与合并
    file_path = "E:\测试\拆分表\"  '待合并工作簿所在的文件夹
    file_name = Dir(file_path & "*.xlsx")
    Application.ScreenUpdating = False
    Application.DisplayAlerts = False
    Set dict = CreateObject("scripting.dictionary")
    Set write_wb = Workbooks.Add    '新建工作簿,合并文件
    '新建工作簿默认工作表,防止有同名被合并表,导致整表复制后名称改变;但会缺少表头
    For Each sht In write_wb.Worksheets
        dict(sht.Name) = ""
    Next
    Do While file_name <> ""
        Set wb = Workbooks.Open(file_path & file_name)
        For Each sht In wb.Worksheets
            If Not dict.Exists(sht.Name) Then  '不存在的,直接复制整表
                dict(sht.Name) = ""
                sht.Copy After:=write_wb.Sheets(write_wb.Sheets.count)
            Else
                Set write_ws = write_wb.Worksheets(sht.Name)
                '首行为空,会导致后续数据被覆盖
                If WorksheetFunction.CountA(write_ws.Rows(1)) = 0 Then write_ws.Rows(1).Delete
                write_row = write_ws.UsedRange.Rows.count + 1  '合并工作表的第一个空行写入
                sheet_row = sht.UsedRange.Rows.count
                sht.Rows(title_row + 1 & ":" & sheet_row - end_row).Copy write_ws.Range("A" & write_row)
            End If
            'Exit Do
        Next
        wb.Close (False)
        file_name = Dir  '下一个文件名
    Loop
    '保存文件
    save_file = file_path & "合并表.xlsx"
    write_wb.SaveAs filename:=save_file
    write_wb.Close (False)
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
End Sub

4.1,合并且显示原工作簿名称

2022.8.27更新,应评论建议
增加在A列显示原工作簿名称;因按同名工作表合并,故没有显示原工作表名称的必要
2023.5.23更新,应评论建议
为避免工作表公式因复制粘贴导致引用错误,新增粘贴为数值功能,且不改变工作表格式

Sub 合并文件夹下所有工作簿中同名工作表1()
    '文件夹下所有工作簿wb所有工作表ws按名称合并保存至新建工作表(但不含子文件夹),但不修改原数据(工作表格式相同)
    Dim dict As Object, sht As Worksheet, fso As Object, only_value As Boolean
    Dim file_path$, file_name$, title_row&, end_row&, save_file$
    Dim write_row&, sht_row&, sht_col&, copy_row&, temp, r&
'--------------------参数填写:title_row,数字,第1行为1向下递增;end_row,数字
    title_row = 1: end_row = 0  '表头、表尾行数,不参与合并
    only_value = True  '仅粘贴为数值,是/否
    file_path = "E:\测试\拆分表\"  '待合并工作簿所在的文件夹
    file_name = Dir(file_path & "*.xlsx")
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set dict = CreateObject("scripting.dictionary")
    Set write_wb = Workbooks.Add    '新建工作簿,合并文件
    '新建工作簿默认工作表,防止有同名被合并表,导致整表复制后名称改变;但会缺少表头
    For Each sht In write_wb.Worksheets
        dict(sht.Name) = "": sht.[a1] = "原工作簿名称"
    Next
    Do While file_name <> ""
        Set wb = Workbooks.Open(file_path & file_name)
        For Each sht In wb.Worksheets
            If Not dict.Exists(sht.Name) Then  '不存在的,直接复制整表
                dict(sht.Name) = "": temp = sht.UsedRange.Value
                sht.Copy After:=write_wb.Sheets(write_wb.Sheets.Count)
                With ActiveSheet
                    .Columns(1).Insert: [a1] = "原工作簿名称"  '插入列
                    If end_row > 0 Then  '删除表尾行
                        r = .UsedRange.Rows.Count
                        .Cells(r, 1).Offset(-end_row + 1, 0).Resize(end_row, 1).EntireRow.Delete
                    End If
                    .Cells(title_row + 1, 1).Resize(.UsedRange.Rows.Count - title_row, 1) = fso.GetBaseName(file_name)  '需要扩展名可直接赋值file_name
                    If only_value Then .[b1].Resize(UBound(temp) - end_row, UBound(temp, 2)) = temp
                End With
            Else
                With write_wb.Worksheets(sht.Name)
                    If WorksheetFunction.CountA(.Rows(1)) = 0 Then .Rows(1).Delete
                    write_row = .UsedRange.Rows.Count + 1  '合并工作表的第一个空行写入
                    sht_row = sht.UsedRange.Rows.Count: sht_col = sht.UsedRange.Columns.Count
                    copy_row = sht_row - title_row - end_row  '复制行数
                    temp = sht.Cells(title_row + 1, "a").Resize(copy_row, sht_col).Value
                    sht.Cells(title_row + 1, "a").Resize(copy_row, sht_col).Copy .Cells(write_row, "b")
                    If only_value Then .Cells(write_row, "b").Resize(copy_row, sht_col) = temp
                    .Cells(write_row, "a").Resize(copy_row) = fso.GetBaseName(file_name)
                End With
            End If
        Next
        wb.Close (False)  '关闭工作簿
        file_name = Dir  '下一个文件名
    Loop
    For Each sht In write_wb.Worksheets  '删除空表ws
        If sht.UsedRange.Rows.Count = 1 Then sht.Delete
    Next
    '保存文件
    save_file = file_path & "合并表.xlsx"
    write_wb.SaveAs filename:=save_file
    write_wb.Close (False)
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
End Sub
举例


合并《Excel·VBA按列拆分工作表、工作簿》,sub3(工作簿按列拆分)拆分后的工作簿
在这里插入图片描述
在这里插入图片描述
合并参数:title_row = 1,end_row = 0,合并后
在这里插入图片描述

5,合并文件夹下所有工作簿中所有工作表,横向汇总数据

对格式相同的工作表进行合并,横向汇总数据(注意:如果数据量较大,需要修改arr数组的Resize大小)

Sub 合并文件夹下所有工作簿中所有工作表_横向汇总数据()
    '多列键汇总单列数据,适用工作表格式相同、待汇总数据为最后一列
    Dim dict As Object, sht As Worksheet, title_col&, key_col, s_row&
    Dim file_path$, file_name$, arr, brr, temp$, w_row&, w_col&, i&, j&, r&
'--------------------参数填写:title_col、key_col、s_row,大于0的整数
    title_col = 3    '表头列数,每个拆分后的sheet都保留,数值
    key_col = Array(2, 3)  '关键值列,按该列的值相同的进行合并,数值数组
    s_row = 2        '数据遍历开始行号
    file_path = "E:\测试\拆分表\"  '待合并工作簿所在的文件夹
    file_name = Dir(file_path & "*.xlsx")
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    Set dict = CreateObject("scripting.dictionary")
    Set write_wb = Workbooks.Add    '新建工作簿,合并文件
    Set write_ws = ActiveSheet: write_ws.Name = "合并表"
    
    Do While file_name <> ""
        Set wb = Workbooks.Open(file_path & file_name)
        For Each sht In wb.Worksheets
            With sht
                If WorksheetFunction.CountA(.UsedRange.Cells) <> 0 Then  '非空工作表
                    If w_col = 0 Then
                        arr = .[a1].CurrentRegion.Resize(10 ^ 3, 10 ^ 2)  'arr最大化,写入行列号初始化
                        w_row = .[a1].CurrentRegion.Rows.Count: w_col = .[a1].CurrentRegion.Columns.Count
                        For i = s_row To w_row
                            temp = ""
                            For Each k In key_col
                                temp = temp & "-" & arr(i, k)
                            Next
                            temp = Mid(temp, 2): dict(temp) = i  '键去除开头的"-",值为行号
                        Next
                    Else
                        brr = .[a1].CurrentRegion
                        w_col = w_col + 1: arr(1, w_col) = brr(1, UBound(brr, 2))  '新增列号
                        For i = s_row To UBound(brr)
                            temp = ""
                            For Each k In key_col
                                temp = temp & "-" & brr(i, k)
                            Next
                            temp = Mid(temp, 2)
                            If Not dict.exists(temp) Then  '新增关键值
                                w_row = w_row + 1: dict(temp) = w_row  '新增行号
                                For j = 1 To UBound(brr, 2) - 1
                                    arr(w_row, j) = brr(i, j)
                                Next
                            End If
                            r = dict(temp): arr(r, w_col) = brr(i, UBound(brr, 2))  '写入数据
                        Next
                    End If
                End If
            End With
        Next
        wb.Close (False)
        file_name = Dir  '下一个文件名
    Loop
    write_ws.[a1].Resize(w_row, w_col) = arr
    write_wb.SaveAs filename:=file_path & "合并表.xlsx"
    write_wb.Close (False)
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
End Sub

举例

在这里插入图片描述

对文件夹下多个工作簿、每个工作簿下有多个工作表、每个工作表格式相同,横向汇总某一列数据。且不同工作簿可能存在行数不同的情况,按字典键值汇总,但不进行计算,结果如下:
在这里插入图片描述

6,合并子文件夹所有工作簿中所有工作表,纵向汇总数据

适用所有工作簿中的所有工作表格式都相同的合并,每个子文件夹生成一个工作表,工作表中包含该子文件夹所有工作簿数据,子文件夹名命名该工作表

不仅可以在A列显示 “原工作簿名称” 、B列显示 “原工作表名称” 信息,同时参数old_name = False也可将该信息删除

新增可指定合并至指定文件的功能,而文章之前内容只能合并至固定新建文件。其优势在于: 遍历子文件夹获取的子文件夹名顺序,及其自动生成的工作表名称顺序,不一定是自己想要的/实际看到的,如果事先自建合并文件,可自行修改工作表名称、顺序,然后运行代码,最终生成的文件内工作表顺序就是自己想要的

Sub 合并文件夹下子文件夹所有工作簿中所有工作表_纵向汇总数据()
    '最终合并文件sheet以子文件夹命名,适用工作表格式相同
    '合并文件A列显示原工作簿名称,B列显示原工作表名称;新增可指定合并文件
    Dim dict As Object, sht As Worksheet, fso As Object, only_value As Boolean, old_name As Boolean
    Dim file_path$, file_name$, title_row&, end_row&, save_file$, s$, title_name, wb As Workbook
    Dim write_row&, sht_row&, sht_col&, copy_row&, nrr, p, f, temp, r&, write_wb As Workbook
'--------------------参数填写:title_row,数字,第1行为1向下递增;end_row,数字
    title_row = 1: end_row = 0  '表头、表尾行数,不参与合并
    'file_path待合并的子文件夹所在文件夹;file_name合并至指定文件,为空""或注释则自动生成文件
    file_path = "E:\测试\拆分表\"
    'file_name = "E:\测试\拆分表\指定合并表.xlsx"
    only_value = True  '仅粘贴为数值,是/否
    old_name = True    '写入原工作簿、工作表名称,是/否
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    Set dict = CreateObject("scripting.dictionary"): delimiter = Chr(28)
    Set fso = CreateObject("Scripting.FileSystemObject"): tm = Timer
    If Len(file_name) Then  'file_name不为空则打开,为空则新建工作簿,即合并文件
        Set write_wb = Workbooks.Open(file_name)
    Else
        Set write_wb = Workbooks.Add
    End If
    '新建工作簿默认工作表,防止有同名被合并表,导致整表复制后名称改变;并清空表格
    title_name = Array("原工作簿名称", "原工作表名称")
    For Each sht In write_wb.Worksheets
        dict(sht.Name) = "": sht.UsedRange.Delete: sht.[a1].Resize(1, 2) = title_name
    Next
    For Each f In fso.GetFolder(file_path).SubFolders  '获取所有子文件夹名
        s = s & delimiter & f.Name
    Next
    fd = Split(Mid(s, 2), delimiter)
    For Each p In fd
        For Each f In fso.GetFolder(file_path & p).Files  '空文件夹不影响
            If f.Name Like "*.xls*" Then
                Set wb = Workbooks.Open(f)
                For Each sht In wb.Worksheets
                    If Not dict.Exists(p) Then  '子文件夹不存在的,直接复制整表
                        dict(p) = ""
                        sht.Copy After:=write_wb.Sheets(write_wb.Sheets.Count)
                        ActiveSheet.Name = p
                        With write_wb.Worksheets(p)
                            nrr = Array(fso.GetBaseName(f.Name), sht.Name): temp = sht.UsedRange.Value
                            .Columns("a:b").Insert: .[a1].Resize(1, 2) = title_name '插入列
                            If end_row > 0 Then  '删除表尾行
                                r = .UsedRange.Rows.Count
                                .Cells(r, 1).Offset(-end_row + 1, 0).Resize(end_row, 1).EntireRow.Delete
                            End If
                            .Cells(title_row + 1, 1).Resize(.UsedRange.Rows.Count - title_row, 2) = nrr
                            If only_value Then .[c1].Resize(UBound(temp) - end_row, UBound(temp, 2)) = temp
                        End With
                    Else
                        With write_wb.Worksheets(p)
                            nrr = Array(fso.GetBaseName(f.Name), sht.Name)  '需要扩展名可直接赋值f.Name
                            If .UsedRange.Rows.Count = 1 Then  '空表为1
                                temp = sht.UsedRange.Value: sht.UsedRange.Copy .[c1]  '含格式复制
                                If end_row > 0 Then  '删除表尾行
                                    r = .UsedRange.Rows.Count
                                    .Cells(r, 1).Offset(-end_row + 1, 0).Resize(end_row, 1).EntireRow.Delete
                                End If
                                .Cells(title_row + 1, 1).Resize(.UsedRange.Rows.Count - title_row, 2) = nrr
                                If only_value Then .[c1].Resize(UBound(temp) - end_row, UBound(temp, 2)) = temp
                            Else
                                write_row = .UsedRange.Rows.Count + 1  '合并工作表的第一个空行写入
                                sht_row = sht.UsedRange.Rows.Count: sht_col = sht.UsedRange.Columns.Count
                                copy_row = sht_row - title_row - end_row  '复制行数
                                temp = sht.Cells(title_row + 1, "a").Resize(copy_row, sht_col).Value
                                sht.Cells(title_row + 1, "a").Resize(copy_row, sht_col).Copy .Cells(write_row, "c")
                                If only_value Then .Cells(write_row, "c").Resize(copy_row, sht_col) = temp
                                .Cells(write_row, 1).Resize(copy_row, 2) = nrr
                            End If
                        End With
                    End If
                Next
                wb.Close (False)
            End If
        Next
    Next
    For Each sht In write_wb.Worksheets  '删除空表ws
        If sht.UsedRange.Rows.Count = 1 Then sht.Delete
    Next
    If Not old_name Then  '无需写入原工作簿、工作表名称
        For Each sht In write_wb.Worksheets
            sht.Columns("a:b").Delete
        Next
    End If
    If Len(file_name) Then  '保存文件,file_name不为空
        write_wb.Close (True)
    Else
        save_file = file_path & "合并表.xlsx"
        write_wb.SaveAs filename:=save_file
        write_wb.Close (False)
    End If
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
    Debug.Print "文件夹合并完成,用时:" & Format(Timer - tm, "0.00")
End Sub

举例

合并4.1-举例中同样的数据,但是每个文件都放入一个同名文件夹中

合并参数:title_row = 1,end_row = 0,file_name被注释,合并后
在这里插入图片描述
在这里插入图片描述
指定合并表,并自行修改工作表名称、顺序
在这里插入图片描述
合并参数:file_path不变,file_name为指定合并表的文件路径,合并后
在这里插入图片描述

7,合并子文件夹同名工作簿中同名工作表,纵向汇总数据

针对解决本问题:《Excel吧-批量合并同名工作簿》
合并不同文件夹中的同名工作簿,并按工作表名称纵向合并数据。适用同名工作簿中的同名工作表格式相同的合并,按每个工作簿名称生成一个合并工作簿,包含所有子文件夹中所有同名工作簿数据;合并工作簿统一保存在“合并表”文件夹

7.1,实现方法1

采用按子文件夹顺序,依次遍历子文件夹中所有工作簿,因此合并工作簿需要反复打开写入再保存关闭,速度较慢

Sub 合并子文件夹同名工作簿中同名工作表_纵向汇总数据1()
    '最终合并文件sheet以工作簿名命名,适用工作表格式相同;合并文件A列显示原子文件夹名
    Dim dict As Object, sht As Worksheet, fso As Object, only_value As Boolean, old_name As Boolean
    Dim file_path$, save_path$, title_row&, end_row&, save_file$, s$, wb As Workbook
    Dim write_row&, sht_row&, sht_col&, copy_row&, p, f, temp, r&, write_wb As Workbook
'--------------------参数填写:title_row,数字,第1行为1向下递增;end_row,数字
    title_row = 1: end_row = 0  '表头、表尾行数,不参与合并
    file_path = "E:\测试\拆分表\合并工作簿7\"  'file_path待合并的子文件夹所在文件夹
    save_path = file_path + "合并表\"   '合并后的表格保存路径
    only_value = True  '仅粘贴为数值,是/否
    old_name = True    '写入原子文件夹名,是/否
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    Set dict = CreateObject("scripting.dictionary"): delimiter = Chr(28)
    Set fso = CreateObject("Scripting.FileSystemObject"): tm = Timer
    If fso.FolderExists(save_path) Then Debug.Print "保存文件夹已存在,会导致错误,请删除": Exit Sub
    For Each f In fso.GetFolder(file_path).SubFolders  '获取所有子文件夹名
        s = s & delimiter & f.Name
    Next
    fd = Split(Mid(s, 2), delimiter)
    If Not fso.FolderExists(save_path) Then fso.CreateFolder (save_path)  '创建文件夹
    For Each p In fd
        For Each f In fso.GetFolder(file_path & p).Files  '空文件夹不影响
            If f.Name Like "*.xls*" Then
                Set wb = Workbooks.Open(f)
                s = fso.GetBaseName(f.Name)  '工作簿文件名,不带扩展名
                If Not dict.Exists(s) Then   '工作簿不存在的,直接复制整个工作簿
                    Set dict(s) = CreateObject("scripting.dictionary")
                    wb.Worksheets.Copy
                    For Each sht In ActiveWorkbook.Worksheets
                        dict(s)(sht.Name) = "": temp = sht.UsedRange.Value
                        sht.Columns("a:a").Insert: sht.[a1] = "子文件夹"  '插入列
                        If end_row > 0 Then  '删除表尾行
                            r = sht.UsedRange.Rows.Count
                            sht.Cells(r, 1).Offset(-end_row + 1, 0).Resize(end_row, 1).EntireRow.Delete
                        End If
                        sht.Cells(title_row + 1, 1).Resize(sht.UsedRange.Rows.Count - title_row, 1) = p
                        If only_value Then sht.[b1].Resize(UBound(temp) - end_row, UBound(temp, 2)) = temp
                    Next
                    wb.Close (False)
                    save_file = save_path & s & "_合并表.xlsx"  '保存文件,无发打开2个同名文件,故加标识
                    ActiveWorkbook.SaveAs filename:=save_file
                    ActiveWorkbook.Close (False)
                Else
                    Set write_wb = Workbooks.Open(save_path & s & "_合并表.xlsx")
                    For Each sht In wb.Worksheets
                        If Not dict(s).Exists(sht.Name) Then  '工作表不存在,直接复制
                            sht.Copy After:=write_wb.Sheets(write_wb.Sheets.Count)
                            dict(s)(sht.Name) = "": temp = sht.UsedRange.Value
                            With write_wb.Worksheets(sht.Name)
                                .Columns("a:a").Insert: .[a1] = "子文件夹"  '插入列
                                If end_row > 0 Then  '删除表尾行
                                    r = .UsedRange.Rows.Count
                                    .Cells(r, 1).Offset(-end_row + 1, 0).Resize(end_row, 1).EntireRow.Delete
                                End If
                                .Cells(title_row + 1, 1).Resize(.UsedRange.Rows.Count - title_row, 1) = p
                                If only_value Then .[b1].Resize(UBound(temp) - end_row, UBound(temp, 2)) = temp
                            End With
                        Else
                            With write_wb.Worksheets(sht.Name)
                                write_row = .UsedRange.Rows.Count + 1  '合并工作表的第一个空行写入
                                sht_row = sht.UsedRange.Rows.Count: sht_col = sht.UsedRange.Columns.Count
                                copy_row = sht_row - title_row - end_row  '复制行数
                                temp = sht.Cells(title_row + 1, "a").Resize(copy_row, sht_col).Value
                                sht.Cells(title_row + 1, "a").Resize(copy_row, sht_col).Copy .Cells(write_row, "b")
                                If only_value Then .Cells(write_row, "b").Resize(copy_row, sht_col) = temp
                                .Cells(write_row, 1).Resize(copy_row, 1) = p
                            End With
                        End If
                    Next
                    wb.Close (False)
                    write_wb.Close (True)  '保存并关闭
                End If
            End If
        Next
    Next
    If Not old_name Then  '无需写入原子文件夹名
        For Each f In fso.GetFolder(save_path).Files
            Set write_wb = Workbooks.Open(f)
            For Each sht In write_wb.Worksheets
                sht.Columns("a:a").Delete
            Next
            write_wb.Close (True)
        Next
    End If
    For Each f In fso.GetFolder(save_path).Files
        f.Name = Replace(f.Name, "_合并表", "")  '合并文件名删除标识
    Next
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
    Debug.Print "文件夹合并完成,用时:" & Format(Timer - tm, "0.00")
End Sub

7.2,实现方法2

采用每次遍历1个工作簿时,如果是之前从未遍历过的工作簿名称时,直接循环遍历其他子文件夹中的同名工作簿;同时,按子文件夹顺序,依次遍历子文件夹中所有工作簿,确保所有工作簿都被遍历。因为减少了合并工作簿反复打开、保存、关闭的操作,所以速度较实现方法1稍快

Sub 合并子文件夹同名工作簿中同名工作表_纵向汇总数据2()
    '最终合并文件sheet以工作簿名命名,适用工作表格式相同;合并文件A列显示原子文件夹名
    Dim dict As Object, sht As Worksheet, fso As Object, only_value As Boolean, old_name As Boolean
    Dim file_path$, save_path$, title_row&, end_row&, save_file$, s$, wb As Workbook
    Dim write_row&, sht_row&, sht_col&, copy_row&, p, f, temp, r&, pp, ff
'--------------------参数填写:title_row,数字,第1行为1向下递增;end_row,数字
    title_row = 1: end_row = 0  '表头、表尾行数,不参与合并
    file_path = "E:\测试\拆分表\合并工作簿7\"  'file_path待合并的子文件夹所在文件夹
    save_path = file_path + "合并表\"   '合并后的表格保存路径
    only_value = True  '仅粘贴为数值,是/否
    old_name = True    '写入原子文件夹名,是/否
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    Dim pn_dict As Object: Set pn_dict = CreateObject("scripting.dictionary")  '记录已遍历文件名
    Set dict = CreateObject("scripting.dictionary"): delimiter = Chr(28)
    Set fso = CreateObject("Scripting.FileSystemObject"): tm = Timer
    If fso.FolderExists(save_path) Then Debug.Print "保存文件夹已存在,会导致错误,请删除": Exit Sub
    For Each f In fso.GetFolder(file_path).SubFolders  '获取所有子文件夹名
        s = s & delimiter & f.Name
    Next
    fd = Split(Mid(s, 2), delimiter)
    If Not fso.FolderExists(save_path) Then fso.CreateFolder (save_path)  '创建文件夹
    For Each p In fd
        For Each f In fso.GetFolder(file_path & p).Files  '空文件夹不影响
            If f.Name Like "*.xls*" Then
                pn = p & "\" & f.Name: s = fso.GetBaseName(f.Name)  's工作簿文件名,不带扩展名
                If Not pn_dict.Exists(pn) Then  '未遍历
                    Set wb = Workbooks.Open(f): pn_dict(pn) = ""
                    Set dict(s) = CreateObject("scripting.dictionary")
                    wb.Worksheets.Copy   '工作簿不存在的,直接复制整个工作簿
                    With ActiveWorkbook  '复制后的工作簿,合并表
                        For Each sht In .Worksheets
                            dict(s)(sht.Name) = "": temp = sht.UsedRange.Value
                            sht.Columns("a:a").Insert: sht.[a1] = "子文件夹"  '插入列
                            If end_row > 0 Then  '删除表尾行
                                r = sht.UsedRange.Rows.Count
                                sht.Cells(r, 1).Offset(-end_row + 1, 0).Resize(end_row, 1).EntireRow.Delete
                            End If
                            sht.Cells(title_row + 1, 1).Resize(sht.UsedRange.Rows.Count - title_row, 1) = p
                            If only_value Then sht.[b1].Resize(UBound(temp) - end_row, UBound(temp, 2)) = temp
                        Next
                        wb.Close (False)
                        For Each pp In fd  '遍历所有子文件夹同名工作簿
                            For Each ff In fso.GetFolder(file_path & pp).Files
                                If ff.Name Like s & ".xls*" Then
                                    pn = pp & "\" & ff.Name
                                    If Not pn_dict.Exists(pn) Then  '未遍历
                                        Set wb = Workbooks.Open(ff): pn_dict(pn) = ""
                                        For Each sht In wb.Worksheets
                                            If Not dict(s).Exists(sht.Name) Then  '工作表不存在,直接复制
                                                sht.Copy After:=.Sheets(.Sheets.Count)
                                                dict(s)(sht.Name) = "": temp = sht.UsedRange.Value
                                                With .Worksheets(sht.Name)
                                                    .Columns("a:a").Insert: .[a1] = "子文件夹"  '插入列
                                                    If end_row > 0 Then  '删除表尾行
                                                        r = .UsedRange.Rows.Count
                                                        .Cells(r, 1).Offset(-end_row + 1, 0).Resize(end_row, 1).EntireRow.Delete
                                                    End If
                                                    .Cells(title_row + 1, 1).Resize(.UsedRange.Rows.Count - title_row, 1) = pp
                                                    If only_value Then .[b1].Resize(UBound(temp) - end_row, UBound(temp, 2)) = temp
                                                End With
                                            Else
                                                With .Worksheets(sht.Name)
                                                    write_row = .UsedRange.Rows.Count + 1  '合并工作表的第一个空行写入
                                                    sht_row = sht.UsedRange.Rows.Count: sht_col = sht.UsedRange.Columns.Count
                                                    copy_row = sht_row - title_row - end_row  '复制行数
                                                    temp = sht.Cells(title_row + 1, "a").Resize(copy_row, sht_col).Value
                                                    sht.Cells(title_row + 1, "a").Resize(copy_row, sht_col).Copy .Cells(write_row, "b")
                                                    If only_value Then .Cells(write_row, "b").Resize(copy_row, sht_col) = temp
                                                    .Cells(write_row, 1).Resize(copy_row, 1) = pp
                                                End With
                                            End If
                                        Next
                                        wb.Close (False)
                                    End If
                                End If
                            Next
                        Next
                        If Not old_name Then  '无需写入原子文件夹名
                            For Each sht In .Worksheets
                                sht.Columns("a:a").Delete
                            Next
                        End If
                        .SaveAs filename:=save_path & s & ".xlsx"
                        .Close (False)
                    End With
                End If
            End If
        Next
    Next
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
    Debug.Print "文件夹合并完成,用时:" & Format(Timer - tm, "0.00")
End Sub

举例,2种实现方法对比

合并4.1-举例中同样的数据,但是所有5个工作簿都放入12个文件夹中,文件夹依次命名为“1月-12月”
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
需要合并的数据共有,60个工作簿180个工作表
合并参数:title_row = 1,end_row = 0,合并后
在这里插入图片描述
在这里插入图片描述
2种实现方法代码运行速度对比: 60个工作簿180个工作表
方法1多次运行,用时在40-60秒之间
方法2多次运行,用时在22.5-29秒之间
2种实现方法生成的合并工作簿结果完全一致,但方法2速度更快。在代码行数差不多的情况下,不同的遍历方式对运行速度的影响较大