VBA汇总多个Sheet数据
时间:2022-07-22
本文章向大家介绍VBA汇总多个Sheet数据,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
1、需求:
有1个工作簿,多个工作表,格式一致,按某列作为关键字(具有唯一性),汇总数据,以工作表名称作为汇总后的新列名称,并生成1列合计。
2、实际例子:
有1个记录员工工资的工作簿,姓名是唯一的,需要汇总每一个人当年的工资数据,举例3个月的数据:
3个月中,人员也会有变动。
需要的结果表:
3、代码实现
简单分析:
- 读取数据
- 根据姓名确定数据要存放的行号,并累加到合计列
- 输出
个人碰到的很多VBA实际问题基本都可以按这3步完成,所以我习惯首先把代码的框架搭好,而且我基本固定按这个模式了:
'函数返回值
Enum RetCode
ErrRT = -1
SuccRT = 1
End Enum
'标记一些位置信息
Enum Pos
RowStart = 2
KeyCol
Cols
End Enum
Type DataStruct
Src() As Variant
Rows As Long
Cols As Long
End Type
Sub vba_main()
Dim d As DataStruct
If RetCode.ErrRT = ReadSrc(d) Then Exit Sub
If RetCode.ErrRT = GetResult(d) Then Exit Sub
End Sub
Private Function GetResult(d As DataStruct) As RetCode
End Function
Private Function ReadSrc(d As DataStruct) As RetCode
ReadSrc = ReadData(d.Src, d.Rows, Pos.Cols, Pos.KeyCol, Pos.RowStart)
End Function
Private Function ReadData(ByRef RetArr() As Variant, ByRef RetRow As Long, Cols As Long, KeyCol As Long, RowStart As Long) As RetCode
ActiveSheet.AutoFilterMode = False
RetRow = Cells(Cells.Rows.Count, KeyCol).End(xlUp).Row
If RetRow < RowStart Then
MsgBox "没有数据"
ReadData = RetCode.ErrRT
Exit Function
End If
RetArr = Cells(1, 1).Resize(RetRow, Cols).Value
ReadData = RetCode.SuccRT
End Function
再根据实际需求来补充完善代码。
完善Pos枚举:
Enum Pos
RowStart = 3
序号 = 1
姓名
科室
工资
KeyCol = 姓名
Cols = 工资
End Enum
注:关于代码里直接使用中文,很多人是不推荐的,因为中文Office版本下写的代码如果放到英文Office版本下会出问题,所以建议是不要使用的。
这里举例就暂不按这个要求。
因为要汇总的表格数量是不确定的,所以vba_main必须要放一个循环语句,-1是因为最后1个表格是输出的汇总表:
For i = 1 To Worksheets.Count - 1
If RetCode.ErrRT = ReadSrc(d) Then Exit Sub
If RetCode.ErrRT = GetResult(d) Then Exit Sub
Next
输出结果我们需要姓名、合计、还有除汇总表之外每一个表都要生成的1列:
Enum PosResult
序号 = 1
姓名
'多个表的列
合计
Cols
End Enum
在这么简单的一个程序里使用Enum、Type等似乎没什么必要,但是一旦养成这种好习惯,你将会发现这有很大的好处。
- 源表格式变化了修改方便:比如这个程序的例子,如果情况变化了,工资表里加了一列工号在姓名前面,那我们又要把程序改写了,如果代码都是按固定的列号写的,改动会比较大,但是使用了Enum的话,只需要在Enum Pos对应位置上加上工号就可以了,修改非常的方便。
- 输出需要新加列方便:比如我们需要把科室新增输出,只要修改PosResult,增加科室,并在GetResult里面增加一行代码就可以。
接下来就只要完成GetResult里的代码就可以了,因为需要知道某个姓名输出的行号,所以使用字典对象是再好不过了,完整代码:
Enum RetCode
ErrRT = -1
SuccRT = 1
End Enum
Enum Pos
RowStart = 3
序号 = 1
姓名
科室
工资
KeyCol = 姓名
Cols = 工资
End Enum
Enum PosResult
序号 = 1
姓名
'多个表的列
合计
Cols
End Enum
Type DataStruct
Src() As Variant
Rows As Long
Cols As Long
shtCount As Long
dic As Object
Result() As Variant
pNextRow As Long
pCol As Long
End Type
Sub vba_main()
Dim d As DataStruct
Dim i As Long
Dim dic As Object
Set d.dic = VBA.CreateObject("Scripting.Dictionary")
d.shtCount = Worksheets.Count - 1
'结果的行数本来应该先用字典遍历一下人名比较合适,这里就偷懒了
'结果的列是固定要有的增加上需要处理的Sheet数量
ReDim d.Result(1 To 1000, 1 To PosResult.Cols + d.shtCount) As Variant
'固定列的标题
d.Result(1, PosResult.序号) = "序号"
d.Result(1, PosResult.姓名) = "姓名"
d.Result(1, PosResult.合计 + d.shtCount) = "合计"
d.pNextRow = 2
For i = 1 To d.shtCount
Worksheets(i).Activate
If RetCode.ErrRT = ReadSrc(d) Then Exit Sub
d.Result(1, PosResult.姓名 + i) = Worksheets(i).Name
d.pCol = PosResult.姓名 + i
If RetCode.ErrRT = GetResult(d) Then Exit Sub
Next
'输出结果
Worksheets("汇总表").Activate
Cells.Clear
Range("A1").Resize(d.pNextRow - 1, PosResult.Cols + d.shtCount).Value = d.Result
MsgBox "OK"
End Sub
Private Function GetResult(d As DataStruct) As RetCode
Dim i As Long
Dim strkey As String
Dim prow As Long
For i = Pos.RowStart To d.Rows
strkey = VBA.CStr(d.Src(i, Pos.姓名))
If d.dic.Exists(strkey) Then
'字典中存在当前的姓名,说明前面已经出现过了,记录前面出现的行
prow = d.dic(strkey)
Else
'没有出现过的时候,就是新行输出
prow = d.pNextRow
'记录到字典中
d.dic(strkey) = prow
'添加新行的人名等信息
d.Result(prow, PosResult.序号) = prow - 1
d.Result(prow, PosResult.姓名) = strkey
'新行往下移
d.pNextRow = d.pNextRow + 1
End If
'添加数据
d.Result(prow, d.pCol) = VBA.CDbl(d.Src(i, Pos.工资))
d.Result(prow, PosResult.合计 + d.shtCount) = VBA.CDbl(d.Src(i, Pos.工资)) + d.Result(prow, PosResult.合计 + d.shtCount)
Next
End Function
Private Function ReadSrc(d As DataStruct) As RetCode
ReadSrc = ReadData(d.Src, d.Rows, Pos.Cols, Pos.KeyCol, Pos.RowStart)
End Function
Private Function ReadData(ByRef RetArr() As Variant, ByRef RetRow As Long, Cols As Long, KeyCol As Long, RowStart As Long) As RetCode
ActiveSheet.AutoFilterMode = False
RetRow = Cells(Cells.Rows.Count, KeyCol).End(xlUp).Row
If RetRow < RowStart Then
MsgBox "没有数据"
ReadData = RetCode.ErrRT
Exit Function
End If
RetArr = Cells(1, 1).Resize(RetRow, Cols).Value
ReadData = RetCode.SuccRT
End Function
- linux下向一个文件中的某行插入数据的做法
- Flash/Flex学习笔记(2):捕获摄像头
- split-brain 脑裂问题(Keepalived)
- Android新手之旅(3) 信息的输出
- Nginx code 状态码说明
- Flash/Flex学习笔记(6):制作基于xml数据源的flv视频播放器
- proxy_pass根据path路径转发时的"/"问题记录
- 温故而知新:查看端口占用情况以及DOS中的管道操作/重定向操作
- Android新手之旅(7) RadioButton的自定义
- Flash/Flex学习笔记(4):如何打开网页及Get/Post数据
- Flash/Flex学习笔记(5):捕获摄像头(续)--在线抓屏并保存到客户端本地
- 分布式监控系统Zabbix--使用Grafana进行图形展示
- Silverlight在线创建PDF(支持中文)
- Flash/Flex学习笔记(3):动态添加组件
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何使用 docker 高效部署 Node 应用
- fish-redux框架路由配置报错问题
- Flutter fish-redux 简单使用
- Flutter 项目.gitignore配置
- js和object的常见操作,持续更新中...
- 常见编程模式之快慢指针
- python pywifi模块——暴力破解wifi
- 面试题系列第3篇:Integer等号判断的内幕,你可能不知道?
- Go by Example 中文:工作池
- 推荐一款万能抓包神器:Fiddler Everywhere
- 猿实战04——el-upload结合nginx之通用图片处理
- 30 多个有内味道且笑死的人代码注释
- Logstash-input-jdbc 同步 mysql 准实时数据至 ElasticSearch 搜索引擎
- 总结一些,我在书写 CSS 的时候,经常犯的错误!
- 通俗理解 set,dict 背后的哈希表