Excel·VBA按列拆分工作表、工作簿

76 篇文章 25 订阅

代码使用说明

  • 代码作用范围:以下代码作用于活动工作簿/工作表,无需将需要拆分的数据保存在启用宏的工作簿中(xlsm格式),只要待拆分表格处于活动状态即可运行代码。同时,也不建议把数据保存在xlsm文件中,vba代码运行结果是无法撤销的
    活动工作簿:如果打开多个工作簿,显示在最前面的就是活动工作簿;活动工作表:活动工作簿当前显示的工作表
  • 代码使用建议:工作表拆分使用方法4,工作簿拆分使用方法3.3;其他几个版本的代码写法较为原始,仅供代码学习参考
  • 扩展名自动获取:以下代码中拆分为工作簿的,使用了自动获取扩展名,是为了方便可同时对xls和xlsx格式拆分。如果无需使用此功能的,可以将代码中的fso.GetExtensionName(wb_name)改为"xlsx",但方法3.3无需此操作
  • RE_STR函数说明:工作簿和工作表的名称中不得包含\/:*?"<>|字符,以下代码使用RE_STR函数删除这些字符。如果能够明确待拆分数据中不包含这些字符的,可以将代码中带有RE_STR的行删除;否则使用代码必须复制本函数,避免报错
    方法3.2:带有RE_STR的行删除后,原文代码第50行中的file_name需要改为CStr(k)
    方法3.3:带有RE_STR的行删除后,原文代码第46行中的file_name需要改为CStr(k)
    方法4:仅需删除带有RE_STR的行
Function RE_STR(ByVal source_str$, pat$, Optional replace_str$ = "$1")
    '通用正则替换函数,函数定义RE(字符串,正则模式,替换值)对单元格返回正则替换后的字符串
    With CreateObject("vbscript.regexp")  '正则表达式
        .Global = True
        .Pattern = pat
        RE_STR = .Replace(source_str, replace_str)
    End With
End Function

1,工作表按列拆分为工作表

改进《将excel按照某一列拆分成多个文件》,使代码更具通用性,可以实现将工作表拆分为工作表或工作簿,仅支持单列关键值

Sub 工作表按列拆分为工作表()
    '当前工作表(worksheet)按固定某列的值拆分为多个工作表,保存在当前工作簿(workbook)
    Dim arr, dict As Object
    Set dict = CreateObject("scripting.dictionary")
'--------------------参数填写:num_col,数字,A列为1向右递增;title_row,数字,第1行为1向下递增
    num_col = 4  '关键值列,按该列的值进行拆分,相同的保存在同一ws
    title_row = 1  '表头行,每个拆分后的sheet都保留
    Set ws = Application.ActiveSheet
    arr = ActiveSheet.UsedRange  '所有数据行读取为数组,也可arr = [a1].CurrentRegion
    
    For i = title_row + 1 To UBound(arr):  '遍历关键值列,写入字典,key为关键值,item为对应的行
        If Not dict.Exists(arr(i, num_col)) Then  '新键-值
            Set dict(arr(i, num_col)) = Rows(i)
        Else  '已有键-值,更新
            Set dict(arr(i, num_col)) = Union(dict(arr(i, num_col)), Rows(i))
        End If
    Next
    k = dict.Keys:v = dict.Items
    For i = 0 To dict.count - 1:  '遍历字典,创建、写入ws
        'Worksheets.Add(after:=Sheets(Sheets.count)).Name = "拆分表" & i + 1  '最后添加新sheet,序号命名
        Worksheets.Add(after:=Sheets(Sheets.count)).Name = "拆分表_" & k(i)  '最后添加新sheet,keys命名
        With ActiveSheet
            ws.Rows(1).Copy
            .[a1].PasteSpecial Paste:=xlPasteColumnWidths  '复制列宽
            ws.Rows(1 & ":" & title_row).Copy .[a1]  '复制表头
            v(i).Copy .Range("A" & title_row + 1)  '复制数据
        End With
        'Exit For  '强制退出for循环,单次测试使用
    Next
End Sub

2,工作表按列拆分为工作簿

单列关键值

Sub 工作表按列拆分为工作簿()
    '当前工作表(worksheet)按固定某列的值拆分为多个工作簿(workbook),文件单独保存
    Dim arr, dict As Object
    Set dict = CreateObject("scripting.dictionary"): tm = Timer
    Set fso = CreateObject("Scripting.FileSystemObject")
'--------------------参数填写:num_col,数字,A列为1向右递增;title_row,数字,第1行为1向下递增
    num_col = 4  '关键值列,按该列的值进行拆分,相同的保存在同一ws
    title_row = 1  '表头行,每个拆分后的sheet都保留
    Set ws = Application.ActiveSheet
    wb_path = Application.ActiveWorkbook.Path  '当前工作簿文件路径
    wb_name = Application.ActiveWorkbook.Name  '当前工作簿文件名和扩展名
    save_path = wb_path + "\拆分表\"  '保存拆分后的表格保存路径
    If Not fso.FolderExists(save_path) Then fso.CreateFolder (save_path)  '创建文件夹
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    
    arr = ActiveSheet.UsedRange  '所有数据行读取为数组,也可arr = [a1].CurrentRegion
    For i = title_row + 1 To UBound(arr):  '遍历关键值列,写入字典,key为关键值,item为对应的行
        If Not dict.Exists(arr(i, num_col)) Then  '新键-值
            Set dict(arr(i, num_col)) = Rows(i)
        Else  '已有键-值,更新
            Set dict(arr(i, num_col)) = Union(dict(arr(i, num_col)), Rows(i))
        End If
    Next
    k = dict.Keys:v = dict.Items
    For i = 0 To dict.count - 1:  '遍历字典,创建、写入wb
        Workbooks.Add
        With ActiveSheet
            ws.Rows(1).Copy
            .[a1].PasteSpecial Paste:=xlPasteColumnWidths  '复制列宽
            ws.Rows(1 & ":" & title_row).Copy .[a1]  '复制表头
            v(i).Copy .Range("A" & title_row + 1)  '复制数据
        End With
        '保存文件全名(文件路径、文件名、扩展名),keys命名
        save_file = save_path & fso.GetBaseName(wb_name) & "_拆分表_" & k(i) & "." & fso.GetExtensionName(wb_name)
        ActiveWorkbook.SaveAs filename:=save_file
        ActiveWorkbook.Close (False)
        'Exit For  '强制退出for循环,单次测试使用
    Next
    Set fso = Nothing  '释放内存
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
    Debug.Print "工作表已拆分完成,累计用时" & Format(Timer - tm, "0.00")  '耗时
End Sub

1、2举例

原始数据
在这里插入图片描述
拆分为工作表
在这里插入图片描述
在这里插入图片描述
拆分为工作薄
原始数据

3,工作簿按列拆分

对包含多个工作表的工作簿进行拆分,支持每个工作表中关键值列号都不同(单列关键值)

3.1,复制法

Sub 工作簿按列拆分()
    '当前工作簿wb所有工作表ws按一列的值拆分为多个工作簿,新旧工作簿形式一致,以列值命名新wb
    Dim arr, dict As Object, fso As Object, title_row&, num_col&, i&
'--------------------参数填写:num_col,数字,A列为1向右递增;title_row,数字,第1行为1向下递增
    title_row = 1  '表头行,每个拆分后的sheet都保留
    num_col = 0    '关键值列,按该列的值进行拆分,相同的保存在同一ws,为0时使用key_col
    key_col = "属地"  '首行关键值,当各工作表关键值列号不同时,使用关键值动态确定num_col(初始为0)
    Set dict = CreateObject("scripting.dictionary"): tm = Timer
    Set fso = CreateObject("Scripting.FileSystemObject")
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    
    With ActiveWorkbook  '拆分当前工作簿
        save_path = .path + "\拆分表\"  '保存拆分后的表格保存路径
        wb_name = .Name  '当前工作簿文件名和扩展名
        If Not fso.FolderExists(save_path) Then fso.CreateFolder (save_path)  '创建文件夹
        For Each sht In .Worksheets
            If num_col > 0 Then
                col = num_col
            ElseIf num_col = 0 Then  '为0时使用key_col动态确定num_col
                For i = 1 To sht.UsedRange.Columns.Count
                    If sht.Cells(1, i).Value = key_col Then col = i
                Next
            End If
            arr = sht.UsedRange
            For i = title_row + 1 To UBound(arr)  '遍历关键值列,写入字典,key为关键值,item为对应的行
                If Len(arr(i, col)) > 0 Then      '关键值列不为空
                    If Not dict.Exists(arr(i, col)) Then  '新键-值
                        Set dict(arr(i, col)) = sht.Rows(i)
                    Else  '已有键-值,更新
                        Set dict(arr(i, col)) = Union(dict(arr(i, col)), sht.Rows(i))  'Union,range对象
                    End If
                End If
            Next
            k = dict.keys: v = dict.Items
            For i = 0 To dict.Count - 1:  '遍历字典,创建、写入wb
                Workbooks.Add
                With ActiveSheet
                    .Name = sht.Name  '工作表命名
                    sht.Rows(1).Copy
                    .[a1].PasteSpecial Paste:=xlPasteColumnWidths  '复制列宽
                    sht.Rows(1 & ":" & title_row).Copy .[a1]       '复制表头
                    v(i).Copy .Range("A" & title_row + 1)          '复制数据
                End With
                Set ws = Application.ActiveSheet
                '保存文件全名(文件路径、文件名、扩展名),keys命名
                file_name = RE_STR(CStr(k(i)), "[\\/:*?""<>|]", "")  '删除文件名非法字符
                save_file = save_path & file_name & "." & fso.GetExtensionName(wb_name)
                If Not fso.FileExists(save_file) Then  '文件不存在,创建
                    ActiveWorkbook.SaveAs filename:=save_file
                    ActiveWorkbook.Close (False)
                Else  '文件存在,复制
                    Set save_wb = Application.Workbooks.Open(save_file)  '打开文件
                    ws.Copy After:=Sheets(save_wb.Sheets.Count)
                    save_wb.Close (True)
                    ActiveWorkbook.Close (False)
                End If
            Next
            dict.RemoveAll  '清空字典
        Next
    End With
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
    Debug.Print "工作簿已拆分完成,累计用时" & Format(Timer - tm, "0.00")  '耗时
End Sub
举例

1个工作簿中有3个工作表,需要按照“属地”所在列的值拆分整个工作簿
在这里插入图片描述
工作簿拆分结果
在这里插入图片描述
在这里插入图片描述

3.2,删除法

以上工作簿按列拆分采用的是复制数据的方法,以下为删除法,删除非同一关键值的行。
经测试,删除法比原本的复制法快2倍以上,尤其是使用先Union行再删除的方法

2023.4.17更新,应评论建议
为避免某个工作表仅存在单一关键值而无需执行删除操作导致报错的,更新增加if判断以避免
同时在某个工作表执行删除操作后仅有表头行的空表情况,更新增加删除此类空表

Sub 工作簿按列拆分_删除法()
    '当前工作簿wb所有工作表ws按一列的值拆分为多个工作簿,新旧工作簿形式一致,以列值命名新wb
    '采用删除非同一关键值的方法;同时使用字典定义参数,可实现每个ws表头行数与关键值列号都不同
    Dim arr, args_dict As Object, dict As Object, fso As Object, rng As Range, t&, c&, i&
    Set args_dict = CreateObject("scripting.dictionary")  '参数字典
'--------------------参数填写:字典(工作表名)= Array(表头行数, 关键值列号);如果工作表名未在字典中,则不拆分
    args_dict("A级") = Array(1, 4): args_dict("B级") = Array(1, 3): args_dict("C级") = Array(1, 3)
    Set dict = CreateObject("scripting.dictionary"): tm = Timer
    Set fso = CreateObject("Scripting.FileSystemObject")
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    
    With ActiveWorkbook  '拆分当前工作簿
        For Each sht In .Worksheets  '遍历所有工作表获取所有关键值
            If args_dict.Exists(sht.Name) Then  '如果工作表名未在参数字典中,则不拆分
                arr = sht.UsedRange: t = args_dict(sht.Name)(0): c = args_dict(sht.Name)(1)
                For i = t + 1 To UBound(arr)
                    If Len(arr(i, c)) > 0 Then dict(arr(i, c)) = ""  '关键值列不为空
                Next
            End If
        Next
        save_path = .path + "\拆分表\"  '保存拆分后的表格保存路径
        wb_name = .Name  '当前工作簿文件名和扩展名
        If Not fso.FolderExists(save_path) Then fso.CreateFolder (save_path)  '创建文件夹
        For Each k In dict.keys
            Set write_wb = Workbooks.Add  '新建工作簿,拆分文件
            For Each sht In .Worksheets
                If args_dict.Exists(sht.Name) Then
                    sht.Copy After:=write_wb.Worksheets(write_wb.Worksheets.Count)
                    With write_wb.Worksheets(write_wb.Worksheets.Count)
                        arr = .UsedRange: t = args_dict(sht.Name)(0): c = args_dict(sht.Name)(1)
                        For i = t + 1 To UBound(arr)
                            If arr(i, c) <> k Then
                                If rng Is Nothing Then
                                    Set rng = .Rows(i)
                                Else
                                    Set rng = Union(rng, .Rows(i))
                                End If
                            End If
                        Next
                        '删除非同一关键值的行,清空变量;删除仅有表头的空表
                        If Not rng Is Nothing Then rng.Delete: Set rng = Nothing
                        If .UsedRange.Rows.Count = t Then .Delete
                    End With
                End If
            Next
            write_wb.Worksheets(1).Delete  'excel新建wb第1个ws为空表
            '保存文件全名(文件路径、文件名、扩展名),keys命名
            file_name = RE_STR(CStr(k), "[\\/:*?""<>|]", "")  '删除文件名非法字符
            save_file = save_path & file_name & "." & fso.GetExtensionName(wb_name)
            write_wb.SaveAs filename:=save_file
            write_wb.Close (False)
        Next
    End With
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
    Debug.Print "工作簿已拆分完成,累计用时" & Format(Timer - tm, "0.00")  '耗时
End Sub

3.3,删除法,改进版

2023.10.21更新,在方法3.2的删除法的基础上,与方法4的工作表整体复制相结合——经测试,删除法改进版比原版快1倍以上
2023.12.29更新,应评论建议
对关键列中错误值不进行拆分,忽略错误值所在列,避免报错,错误值包含非字符串类型的#N/A#DIV/0!#VALUE!

Sub 工作簿按列拆分_删除法2()
    '当前工作簿wb所有工作表ws按一列的值拆分为多个工作簿,新旧工作簿形式一致,以列值命名新wb
    Dim arr, args_dict As Object, dict As Object, fso As Object, rng As Range, t&, c&, i&
    Dim sht As Worksheet, write_wb As Workbook, save_path$, file_name$, srr, k
    Set args_dict = CreateObject("scripting.dictionary")  '参数字典
'--------------------参数填写:字典(工作表名)= Array(表头行数, 关键值列号);如果工作表名未在字典中,则不拆分
    args_dict("A级") = Array(1, 4): args_dict("B级") = Array(1, 3): args_dict("C级") = Array(1, 3)
    Set dict = CreateObject("scripting.dictionary"): tm = Timer
    Set fso = CreateObject("Scripting.FileSystemObject")
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    
    With ActiveWorkbook  '拆分当前工作簿
        For Each sht In .Worksheets  '遍历所有工作表获取所有关键值
            If args_dict.Exists(sht.Name) Then  '如果工作表名未在参数字典中,则不拆分
                arr = sht.UsedRange: t = args_dict(sht.Name)(0): c = args_dict(sht.Name)(1)
                For i = t + 1 To UBound(arr)
                    If TypeName(arr(i, c)) <> "Error" Then
                        If Len(arr(i, c)) > 0 Then dict(arr(i, c)) = ""  '关键值列不为空
                    End If
                Next
            End If
        Next
        save_path = .path + "\拆分表\"  '保存拆分后的表格保存路径
        srr = args_dict.keys  '需要拆分的工作表名称数组,注意args_dict中不能有工作簿中不存在的工作表
        If Not fso.FolderExists(save_path) Then fso.CreateFolder (save_path)  '创建文件夹
        For Each k In dict.keys
            .Worksheets(srr).Copy: Set write_wb = ActiveWorkbook  '整体复制工作表
            For Each sht In write_wb.Worksheets
                arr = sht.UsedRange: t = args_dict(sht.Name)(0): c = args_dict(sht.Name)(1)
                For i = t + 1 To UBound(arr)
                    If TypeName(arr(i, c)) = "Error" Then arr(i, c) = ""  '错误值改为空值,便于判断拆分
                    If arr(i, c) <> k Then
                        If rng Is Nothing Then
                            Set rng = sht.Rows(i)
                        Else
                            Set rng = Union(rng, sht.Rows(i))
                        End If
                    End If
                Next
                '删除非同一关键值的行,清空变量;删除仅有表头的空表
                If Not rng Is Nothing Then rng.Delete: Set rng = Nothing
                If sht.UsedRange.Rows.Count = t Then sht.Delete
            Next
            '保存文件全名(文件路径、文件名、扩展名),keys命名
            file_name = RE_STR(CStr(k), "[\\/:*?""<>|]", "")  '删除文件名非法字符
            write_wb.SaveAs filename:=save_path & file_name & ".xlsx"
            write_wb.Close (False)
        Next
    End With
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
    Debug.Print "工作簿已拆分完成,累计用时" & Format(Timer - tm, "0.00")  '耗时
End Sub

4,工作表按列拆分,支持多列关键值

如果需要对数据按多列关键值合并进行拆分,可以选择添加辅助列,先将多列的值合并,在使用以上sub进行拆分;也可以重新定义一个sub既支持单列又支持多列关键值的

2023.4.29更新,应评论建议
对工作表ws拆分为工作簿wb,可在keep_ws数组中指定ws名称,使得每个wb都保留指定名称的ws,以保证拆分后的表格内公式正常使用。经测试表1中vlookup可以正常获取表2的结果
2023.12.29更新,应评论建议
对关键列中错误值转换为空值处理,避免报错,如果按单列关键值进行拆分,那么错误值不会生成拆分结果;如果按多列关键值进行拆分,那么会以错误值转换为空值后的整体为字典键,生成拆分结果。错误值包含非字符串类型的#N/A#DIV/0!#VALUE!

Sub 工作表按列拆分_多列关键值()
    '当前工作表ws按固定多列的值拆分为多个工作表,文件保存在当前工作簿wb同一文件夹下单独文件夹内
    '采用删除法;关键值可单列、多列;可拆分为工作表或工作簿;增加拆分为wb固定保留指定ws
    Dim arr, dict As Object, fso As Object, rng As Range, i&, t&, b&, bb&, k$, ws_name$, file_name$
'--------------------参数填写:key_col,列号数组,数字
    title_row = 1  '表头行,每个拆分后的sheet都保留
    key_col = Array(2, 4)  '关键值列,按该列的值进行拆分,相同的保存在同一ws
    delimiter = "_"    '分隔符,最好为数据中不存在的字符,如Chr(28)或|
    save_type = "wb"   '保存方式:ws拆分为工作表,wb拆分为工作簿
    keep_ws = Array("数据源")  '拆分为wb,需固定保留指定ws名称,无需保留的参数为空数组
    ReDim temp(1 To UBound(key_col) - LBound(key_col) + 1)
    Set dict = CreateObject("scripting.dictionary"): tm = Timer
    Set fso = CreateObject("Scripting.FileSystemObject")
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    
    With ActiveSheet
        arr = .UsedRange: ReDim brr(1 To UBound(arr) - title_row)  'brr保存关键字
        For i = title_row + 1 To UBound(arr)  '遍历所有工作表获取所有关键值
            t = 0
            For Each c In key_col
                t = t + 1: temp(t) = arr(i, c)
                If TypeName(temp(t)) = "Error" Then temp(t) = ""  '避免错误值报错
            Next
            k = Join(temp, delimiter): b = b + 1: brr(b) = k
            If Len(k) > 0 Then dict(k) = ""  '关键值不为空
        Next
        If save_type = "ws" Then    '拆分为工作表
            For Each kk In dict.keys
                ws_name = Replace(kk, delimiter, "_")    '将分隔符改为下划线
                ws_name = RE_STR(ws_name, "[\\/:*?""<>|]", "")  '删除文件名非法字符
                .Copy after:=Worksheets(Worksheets.Count)  '复制到最后,keys命名
                With ActiveSheet
                    crr = .UsedRange: bb = 0: .Name = ws_name
                    For i = title_row + 1 To UBound(arr)
                        bb = bb + 1
                        If brr(bb) <> kk Then
                            If rng Is Nothing Then
                                Set rng = .Rows(i)
                            Else
                                Set rng = Union(rng, .Rows(i))
                            End If
                        End If
                    Next
                    If Not rng Is Nothing Then rng.Delete: Set rng = Nothing  '删除非同一关键值的行,清空变量
                End With
            Next
        ElseIf save_type = "wb" Then    '拆分为工作簿
            save_path = .Parent.path + "\拆分表\"  '保存拆分后的表格保存路径
            ws_name = .Name: wb_name = .Parent.Name  '当前ws、wb名称
            If Not fso.FolderExists(save_path) Then fso.CreateFolder (save_path)  '创建文件夹
            For Each kk In dict.keys
                If UBound(keep_ws) = -1 Then  '无需保留固定ws
                    .Copy    'ws在copy后自动生成一个新建wb
                Else
                    s = Join(keep_ws, Chr(28)) & Chr(28) & ws_name  '字符串拼接
                    srr = Split(s, Chr(28))  '需复制的ws名称数组
                    .Parent.Worksheets(srr).Copy  '工作表整体复制
                End If
                With ActiveWorkbook.Worksheets(ws_name)
                    crr = .UsedRange: bb = 0
                    For i = title_row + 1 To UBound(arr)
                        bb = bb + 1
                        If brr(bb) <> kk Then
                            If rng Is Nothing Then
                                Set rng = .Rows(i)
                            Else
                                Set rng = Union(rng, .Rows(i))
                            End If
                        End If
                    Next
                    If Not rng Is Nothing Then rng.Delete: Set rng = Nothing
                End With
                '保存文件全名(文件路径、文件名、扩展名),keys命名
                file_name = Replace(kk, delimiter, "_")    '将分隔符改为下划线
                file_name = RE_STR(file_name, "[\\/:*?""<>|]", "")  '删除文件名非法字符
                save_file = save_path & file_name & "." & fso.GetExtensionName(wb_name)
                ActiveWorkbook.SaveAs filename:=save_file
                ActiveWorkbook.Close (False)
            Next
        End If
    End With
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
    Debug.Print "工作表已拆分完成,累计用时" & Format(Timer - tm, "0.00")  '耗时
End Sub

注意:
关键值列最好不存在为空的单元格,如果分隔符delimiter也为空的话,可能导致关键值错误进而拆分错误,比如
在这里插入图片描述
b1和c1为空值,textjoin分隔符为空则导致关键值d1和d2相同,为避免这种情况delimiter最好不为空,且为数据中不存在的字符,避免最后replace导致保存文件名出错

举例

原始数据
在这里插入图片描述
拆分为工作簿
在这里插入图片描述

5,工作表按列拆分,先拆分为工作簿再拆分为工作表

如果需要对数据按1列关键值拆分为工作簿,再按另1列关键值拆分为工作表,可以使用方法4分2步操作运行代码,也可以重新定义一个sub一次性拆分数据

Sub 工作表按列拆分_先拆分为工作簿再拆分为工作表()
    '当前工作表ws按一列的值拆分为多个工作簿,再按另一列的值拆分为多个工作表,文件保存在当前工作簿wb同一文件夹下单独文件夹内
    '采用删除法;拆分结果可保留指定名称的ws;关键值列忽略错误值、空值
    Dim dict1 As Object, dict2 As Object, fso As Object, rng As Range
    Dim title_row&, wb&, ws&, keep_ws, arr, brr, i&, ws_name$, s$, srr, k, kk
'--------------------参数填写:title_row,wb,ws,keep_ws
    title_row = 1    '表头行,每个拆分后的sheet都保留
    wb = 3: ws = 4    '关键值列号,按wb列拆分为工作簿,按ws列拆分为工作表
    keep_ws = Array("数据源")  '拆分为工作簿后需要保留的指定ws名称,无需保留的参数为空数组(同一工作簿内)
    Set dict1 = CreateObject("scripting.dictionary")
    Set dict2 = CreateObject("scripting.dictionary")
    Set fso = CreateObject("Scripting.FileSystemObject"): tm = Timer
    Application.ScreenUpdating = False  '关闭屏幕更新,加快程序运行
    Application.DisplayAlerts = False   '不显示警告信息
    
    With ActiveSheet  '拆分当前工作表
        save_path = .Parent.path + "\拆分表\"  '保存拆分后的表格保存路径
        If Not fso.FolderExists(save_path) Then fso.CreateFolder (save_path)  '创建文件夹
        arr = .UsedRange: ws_name = .Name
        For i = title_row + 1 To UBound(arr)  '遍历获取所有关键值
            If TypeName(arr(i, wb)) <> "Error" Then
                If Len(arr(i, wb)) > 0 Then dict1(arr(i, wb)) = ""  '关键值不为空
            Else
                arr(i, wb) = ""  '错误值改为空值,便于判断拆分
            End If
        Next
        For Each k In dict1.keys
            If UBound(keep_ws) = -1 Then  '无需保留固定ws
                .Copy    'ws在copy后自动生成一个新建wb
            Else
                s = Join(keep_ws, Chr(28)) & Chr(28) & ws_name  '字符串拼接
                srr = Split(s, Chr(28))  '需复制的ws名称数组
                .Parent.Worksheets(srr).Copy  '工作表整体复制
            End If
            With ActiveWorkbook.Worksheets(ws_name)    '------拆分为工作簿
                For i = title_row + 1 To UBound(arr)
                    If arr(i, wb) <> k Then
                        If rng Is Nothing Then
                            Set rng = .Rows(i)
                        Else
                            Set rng = Union(rng, .Rows(i))
                        End If
                    End If
                Next
                If Not rng Is Nothing Then rng.Delete: Set rng = Nothing  '删除非同一关键值的行,清空变量
                brr = .UsedRange    '------拆分为工作表
                For i = title_row + 1 To UBound(brr)
                    If TypeName(brr(i, ws)) <> "Error" Then
                        If Len(brr(i, ws)) > 0 Then dict2(brr(i, ws)) = ""
                    Else
                        brr(i, ws) = ""
                    End If
                Next
                For Each kk In dict2.keys
                    .Copy after:=Worksheets(.Parent.Worksheets.Count)  '复制到最后,keys命名
                    With ActiveSheet
                        .Name = kk
                        For i = title_row + 1 To UBound(brr)
                            If brr(i, ws) <> kk Then
                                If rng Is Nothing Then
                                    Set rng = .Rows(i)
                                Else
                                    Set rng = Union(rng, .Rows(i))
                                End If
                            End If
                        Next
                        If Not rng Is Nothing Then rng.Delete: Set rng = Nothing
                    End With
                Next
                dict2.RemoveAll  '清空字典
                '保存文件全名(文件路径、文件名、扩展名),keys命名
                .Parent.SaveAs filename:=save_path & k & ".xlsx"
                .Parent.Close (False)
            End With
        Next
    End With
    Application.ScreenUpdating = True: Application.DisplayAlerts = True
    Debug.Print "工作表已拆分完成,累计用时" & Format(Timer - tm, "0.00")  '耗时
End Sub

举例

原始数据
在这里插入图片描述
拆分结果
在这里插入图片描述
在这里插入图片描述

  • 70
    点赞
  • 228
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 77
    评论
以下是一个示例的VBA代码,用于将Excel工作簿拆分成多个工作簿: ```vba Sub SplitWorkbook() Dim OriginalWorkbook As Workbook Dim NewWorkbook As Workbook Dim OriginalWorksheet As Worksheet Dim NewWorksheet As Worksheet Dim Cell As Range Dim RowCounter As Long Dim LastRow As Long Dim SplitColumn As Range Dim UniqueValues As Collection Dim Value As Variant ' 设置原始工作簿工作 Set OriginalWorkbook = ThisWorkbook Set OriginalWorksheet = OriginalWorkbook.Worksheets("Sheet1") ' 替换为您要拆分工作名称 ' 设置拆分列范围 Set SplitColumn = OriginalWorksheet.Range("A:A") ' 替换为您要拆分的列 ' 获取唯一值集合 Set UniqueValues = New Collection On Error Resume Next For Each Cell In SplitColumn UniqueValues.Add Cell.Value, CStr(Cell.Value) Next Cell On Error GoTo 0 ' 遍历唯一值并创建新工作簿 For Each Value In UniqueValues ' 创建新工作簿并复制原始工作的结构和数据 Set NewWorkbook = Workbooks.Add Set NewWorksheet = NewWorkbook.Worksheets(1) OriginalWorksheet.Copy Before:=NewWorksheet ' 删除除唯一值之外的行 With NewWorksheet LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row For RowCounter = LastRow To 2 Step -1 ' 从最后一行开始往上遍历 If .Cells(RowCounter, 1).Value <> Value Then .Rows(RowCounter).Delete End If Next RowCounter End With ' 保存新工作簿 NewWorkbook.SaveAs "路径\" & Value & ".xlsx" ' 替换为您要保存的路径和文件名 ' 关闭新工作簿 NewWorkbook.Close SaveChanges:=False Next Value End Sub ``` 请注意,您需要根据实际情况进行以下修改: 1. 将`"Sheet1"`替换为您要拆分工作名称。 2. 将`"A:A"`替换为您要拆分的列范围。 3. 将`"路径\" & Value & ".xlsx"`替换为您要保存的路径和文件名。 运行此代码后,它将根据指定的列中的唯一值,将原始工作簿拆分为多个新的工作簿,并将每个唯一值命名为文件名。每个新工作簿将只包含与对应唯一值匹配的行。
评论 77
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔_51

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值