第3天实战:循环与数组,让Excel学会"批量思考"!
- 2026-11-01 06:43:02
小张看着自己昨天写的智能验证系统,突然想到一个问题:"老王,如果我有1000行数据要处理,难道要运行1000次吗?"
老王神秘一笑:"问得好!这就是今天要学的——循环。用3行代码处理1000行数据,想学吗?"
小张眼睛一亮:"3行代码处理1000行?这怎么可能!"
"不仅可能,而且还能从5分钟压缩到3秒。"老王打开Excel,"昨天你教Excel'思考',今天教它'批量思考'。"
今日学习目标:掌握VBA批量处理两大神器:循环(For/For Each)和数组,实现从"单打独斗"到"集团作战"的飞跃
01 For循环:让代码学会"重复劳动"
老王在白板上画了一个流程图:"循环,就是让计算机重复做同样的事情。就像工厂的流水线,一个动作重复1000次。"
基础语法:For...Next结构
For 计数器 = 开始值 To 结束值' 重复执行的代码 Next 计数器
实战案例1:批量填充序号
需求:在A列生成1到100的序号
Sub 批量生成序号()Dim i As Long' 循环计数器For i = 1 To 100Cells(i, 1).Value = i' 在A列第i行写入i的值Next iMsgBox "已生成1-100的序号!"End Sub
运行效果:A1=1, A2=2, ..., A100=100
实战案例2:隔行填充颜色
Sub 隔行填充颜色()Dim i As Long' 从第2行开始,到第100行结束,每次+2(只处理偶数行)For i = 2 To 100 Step 2' 第i行整行填充浅蓝色Rows(i).Interior.Color = RGB(204, 255, 255)Next iEnd Sub
关键点:Step 2表示每次循环i增加2,实现隔行处理
实战案例3:倒序处理数据
Sub 倒序处理()Dim i As Long' 从100到1,每次-1For i = 100 To 1 Step -1Cells(i, 2).Value = "第" & i & "行"Next iEnd Sub
老王的小技巧:"Step可以是正数(递增)或负数(递减),灵活运用可以处理各种顺序需求。"
02 For Each循环:优雅地遍历集合
老王拿出一个盒子:"For Each就像打开盒子,一个一个检查里面的物品,不需要知道总共有多少个。"
基础语法:For Each...Next结构
For Each 元素 In 集合' 对每个元素执行操作 Next 元素
实战案例1:遍历工作表
Sub 遍历所有工作表()Dim ws As Worksheet ' 定义工作表变量For Each ws In ThisWorkbook.Worksheets' 在每个工作表的A1单元格写入工作表名称ws.Range("A1").Value = "这是:" & ws.NameNext wsMsgBox "已处理所有" & ThisWorkbook.Worksheets.Count & "个工作表"End Sub
Sub 遍历单元格区域()Dim cell As Range' 定义单元格变量' 遍历A1:A10区域For Each cell In Range("A1:A10")' 如果单元格值大于50,标记为红色If cell.Value > 50 Thencell.Interior.Color = RGB(255, 200, 200)End IfNext cellEnd Sub
For vs For Each:如何选择?
| 知道确切次数 | ||
| 遍历集合对象 | ||
| 需要索引号 | ||
| 处理顺序重要 | ||
| 代码简洁性 |
简单原则:知道次数用For,遍历集合用For Each
03 数组:VBA的"数据集装箱"
老王拿出10个盒子:"如果让你搬10个盒子,你会一个一个搬,还是一次性用推车运?数组就是那个推车。"
数组声明:创建你的"数据集装箱"
' 方法1:声明固定大小的数组Dim 学生名单(1 To 50) As String ' 可以存放50个学生姓名Dim 成绩表(1 To 100, 1 To 5) As Double ' 100行×5列的二维数组' 方法2:声明动态数组(大小可变)Dim 销售数据() As Variant' 方法3:快速声明并赋值Dim 月份 As Variant月份 = Array("1月", "2月", "3月", "4月", "5月", "6月")
数组的基本操作
Sub 数组基础操作()' 1. 声明并初始化数组Dim 水果(4) As String水果(0) = "苹果"水果(1) = "香蕉"水果(2) = "橙子"水果(3) = "葡萄"水果(4) = "芒果"' 2. 遍历数组Dim i As LongFor i = 0 To UBound(水果) ' UBound获取数组最大索引Debug.Print "水果" & (i + 1) & ":" & 水果(i)Next i' 3. 修改数组元素水果(2) = "猕猴桃" ' 把橙子换成猕猴桃' 4. 获取数组信息MsgBox "数组大小:" & UBound(水果) - LBound(水果) + 1 & "个元素"End Sub
二维数组:处理表格数据
Sub 二维数组示例()' 创建一个3行×4列的二维数组Dim 销售表(1 To 3, 1 To 4) As Variant' 填充数据销售表(1, 1) = "产品A"销售表(1, 2) = 100销售表(1, 3) = 50.5销售表(1, 4) = 销售表(1, 2) * 销售表(1, 3) ' 计算金额销售表(2, 1) = "产品B"销售表(2, 2) = 200销售表(2, 3) = 30.2销售表(2, 4) = 销售表(2, 2) * 销售表(2, 3)销售表(3, 1) = "产品C"销售表(3, 2) = 150销售表(3, 3) = 45.8销售表(3, 4) = 销售表(3, 2) * 销售表(3, 3)' 遍历二维数组Dim i As Long, j As LongFor i = 1 To 3For j = 1 To 4Cells(i, j).Value = 销售表(i, j)Next jNext iEnd Sub
重要概念:二维数组就像Excel表格,第一个索引是行,第二个索引是列
04 效率革命:为什么数组比循环快100倍?
老王打开两个Excel文件:"左边这个用传统循环处理5万行数据,右边用数组处理。猜猜哪个快?"
传统方法:一个一个单元格处理
Sub 传统方法_慢速版()Dim startTime As DoublestartTime = Timer ' 记录开始时间Dim lastRow As LonglastRow = Cells(Rows.Count, 1).End(xlUp).RowDim i As LongFor i = 2 To lastRow' 每次操作都访问单元格,非常慢!Cells(i, 5).Value = Cells(i, 3).Value * Cells(i, 4).ValueIf Cells(i, 5).Value > 10000 ThenCells(i, 5).Value = Cells(i, 5).Value * 0.95End IfNext iMsgBox "处理完成!耗时:" & Round(Timer - startTime, 2) & "秒"End Sub
问题:每次循环都要读写Excel单元格,5万次就是5万次I/O操作,极慢!
数组方法:一次性读取,内存计算,一次性写入
Sub 数组方法_极速版()Dim startTime As DoublestartTime = TimerApplication.ScreenUpdating = False ' 关闭屏幕刷新,提升速度Dim lastRow As LonglastRow = Cells(Rows.Count, 1).End(xlUp).Row' 1. 一次性把所有数据读到数组(瞬间完成)Dim arrData As VariantarrData = Range("A1:E" & lastRow).Value' 2. 在内存中批量计算(不碰Excel,速度极快)Dim i As LongFor i = 2 To UBound(arrData, 1) ' UBound获取数组行数' 第5列 = 第3列 × 第4列arrData(i, 5) = arrData(i, 3) * arrData(i, 4)' 如果金额超过10000,打95折If arrData(i, 5) > 10000 ThenarrData(i, 5) = arrData(i, 5) * 0.95End IfNext i' 3. 一次性写回工作表(瞬间完成)Range("A1:E" & lastRow).Value = arrDataApplication.ScreenUpdating = True ' 恢复屏幕刷新MsgBox "处理完成!耗时:" & Round(Timer - startTime, 2) & "秒"End Sub
效率对比:
重要提醒:处理超过1000行数据时,一定要用数组!传统循环会让用户等到怀疑人生。
05 实战升级:批量处理月度销售报表
老王给出真实场景:"公司有12个月的销售数据,每个月一个工作表。需要:1)汇总全年数据;2)计算每月平均;3)找出最佳销售月。"
需求分析:
遍历所有月份工作表 读取每个表的销售数据到数组 在内存中计算汇总、平均等 生成年度报告
完整解决方案:
Sub 生成年度销售报告()' 关闭屏幕刷新和自动计算,大幅提升速度Application.ScreenUpdating = FalseApplication.Calculation = xlCalculationManualDim startTime As DoublestartTime = Timer' 1. 准备汇总数组Dim 月份名称(1 To 12) As StringDim 销售额(1 To 12) As DoubleDim 月份 As WorksheetDim i As Long, 月份索引 As Long' 初始化月份名称月份名称(1) = "1月": 月份名称(2) = "2月": 月份名称(3) = "3月"月份名称(4) = "4月": 月份名称(5) = "5月": 月份名称(6) = "6月"月份名称(7) = "7月": 月份名称(8) = "8月": 月份名称(9) = "9月"月份名称(10) = "10月": 月份名称(11) = "11月": 月份名称(12) = "12月"' 2. 遍历所有月份工作表月份索引 = 1For Each 月份 In ThisWorkbook.Worksheets' 只处理月份工作表(名称包含"月")If InStr(月份.Name, "月") > 0 Then' 一次性读取该月份的所有数据到数组Dim 月份数据 As Variant月份数据 = 月份.UsedRange.Value' 计算该月份销售总额Dim 行 As Long, 列 As LongDim 本月销售额 As Double本月销售额 = 0' 从第2行开始(跳过标题行)For 行 = 2 To UBound(月份数据, 1)' 假设销售额在第3列本月销售额 = 本月销售额 + 月份数据(行, 3)Next 行' 存储到汇总数组销售额(月份索引) = 本月销售额月份索引 = 月份索引 + 1End IfNext 月份' 3. 在"年度报告"工作表中生成汇总Dim 报告 As WorksheetOn Error Resume NextSet 报告 = ThisWorkbook.Worksheets("年度报告")On Error GoTo 0If 报告 Is Nothing Then' 如果"年度报告"工作表不存在,创建它Set 报告 = ThisWorkbook.Worksheets.Add报告.Name = "年度报告"End If' 清空原有数据报告.Cells.Clear' 写入标题With 报告.Range("A1").Value = "月份".Range("B1").Value = "销售额".Range("C1").Value = "占比".Range("D1").Value = "评级"' 设置标题格式With .Range("A1:D1").Font.Bold = True.Interior.Color = RGB(31, 78, 120).Font.Color = RGB(255, 255, 255).HorizontalAlignment = xlCenterEnd WithEnd With' 4. 计算年度总额和每月占比Dim 年度总额 As Double年度总额 = 0For i = 1 To 12年度总额 = 年度总额 + 销售额(i)Next i' 5. 写入月度数据并计算占比Dim 最佳月份 As StringDim 最高销售额 As Double最高销售额 = 0For i = 1 To 12' 写入月份和销售额报告.Cells(i + 1, 1).Value = 月份名称(i)报告.Cells(i + 1, 2).Value = 销售额(i)' 计算占比(百分比)If 年度总额 > 0 Then报告.Cells(i + 1, 3).Value = 销售额(i) / 年度总额报告.Cells(i + 1, 3).NumberFormat = "0.00%"End If' 根据销售额评级If 销售额(i) > 100000 Then报告.Cells(i + 1, 4).Value = "优秀"报告.Cells(i + 1, 4).Interior.Color = RGB(198, 239, 206) ' 绿色ElseIf 销售额(i) > 50000 Then报告.Cells(i + 1, 4).Value = "良好"报告.Cells(i + 1, 4).Interior.Color = RGB(255, 235, 156) ' 黄色Else报告.Cells(i + 1, 4).Value = "待提升"报告.Cells(i + 1, 4).Interior.Color = RGB(255, 199, 206) ' 红色End If' 找出最佳销售月If 销售额(i) > 最高销售额 Then最高销售额 = 销售额(i)最佳月份 = 月份名称(i)End IfNext i' 6. 写入汇总行Dim 最后行 As Long最后行 = 报告.Cells(报告.Rows.Count, 1).End(xlUp).Row + 1报告.Cells(最后行, 1).Value = "年度总计"报告.Cells(最后行, 2).Value = 年度总额报告.Cells(最后行, 3).Value = "100%"报告.Cells(最后行, 3).NumberFormat = "0.00%"报告.Cells(最后行, 4).Value = "最佳月份:" & 最佳月份' 设置汇总行格式With 报告.Rows(最后行).Font.Bold = True.Interior.Color = RGB(255, 255, 0) ' 黄色.Font.Color = RGB(0, 0, 0)End With' 7. 自动调整列宽报告.Columns("A:D").AutoFit' 恢复设置Application.ScreenUpdating = TrueApplication.Calculation = xlCalculationAutomatic' 显示结果Dim 耗时 As Double耗时 = Round(Timer - startTime, 2)MsgBox "年度报告生成完成!" & vbNewLine & _"处理月份数:12个" & vbNewLine & _"最佳销售月:" & 最佳月份 & vbNewLine & _"年度总额:" & Format(年度总额, "#,##0.00") & vbNewLine & _"处理耗时:" & 耗时 & "秒", _vbInformation, "报告生成成功"End Sub
代码亮点解析:
- 数组存储:
用数组存储12个月的销售额,避免重复访问工作表 - 批量读取:
使用 UsedRange.Value一次性读取整个区域到数组 - 内存计算:
所有计算在内存中完成,不操作单元格 - 动态处理:
自动创建"年度报告"工作表 - 性能优化:
关闭屏幕刷新和自动计算
06 今日总结:从"单兵作战"到"集团军作战"
回顾今天学到的核心技能:
| For循环 | |||
| For Each循环 | |||
| 数组处理 |
你今天的成长:
掌握了For循环的三种用法:正序、倒序、自定义步长 学会了用For Each优雅遍历工作表、单元格等集合 理解了数组的一维、二维声明和操作方法 体验了数组处理比传统循环快100倍的效率革命 完成了真实的年度销售报告批量处理项目
最重要的思维转变:
从单个处理到批量处理 从操作单元格到操作内存数组 从肉眼可见的慢到瞬间完成的快
老王看着生成的年度报告,满意地点点头:"现在你写的代码,已经能'批量思考'了。昨天你教Excel如何思考,今天你教它如何高效思考。"
07 明日预告:错误处理与调试,让代码更健壮
小张运行年度报告宏时,突然弹出一个错误:"运行时错误'9':下标越界"。他一脸茫然:"这是什么意思?"
老王笑了:"这就是明天要学的——错误处理。让代码不仅快,还要稳。"
明日核心内容:
- 错误类型:
编译错误、运行时错误、逻辑错误 - 错误处理:
On Error语句的三种用法 - 调试技巧:
断点、监视窗口、立即窗口 - 实战:
让年度报告宏容错性提升10倍
今日作业:
优化今天的年度报告宏,让它能够:
处理任意数量的月份工作表(不一定是12个) 自动识别销售额所在的列(不一定是第3列) 如果某个月份工作表数据为空,跳过而不报错 生成更详细的统计报告:每月环比增长率、季度汇总等
提示:你会需要用到动态数组和更复杂的判断逻辑。明天我们揭晓答案!
最后的话:
真正的编程高手,不是写出能运行的代码,而是写出高效运行的代码。今天你学会了用循环和数组让代码效率提升百倍,这是从"能用"到"好用"的关键一步。
记住:处理数据时,内存 > 数组 > 循环 > 单元格操作。越靠近左边,效率越高。
明天,我们将让代码不仅快,还要稳如泰山。