小张看着自己昨天写的智能验证系统,突然想到一个问题:"老王,如果我有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 100 Cells(i, 1).Value = i ' 在A列第i行写入i的值 Next i MsgBox "已生成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 i End Sub
关键点:Step 2表示每次循环i增加2,实现隔行处理
实战案例3:倒序处理数据
Sub 倒序处理() Dim i As Long ' 从100到1,每次-1 For i = 100 To 1 Step -1 Cells(i, 2).Value = "第" & i & "行" Next i End 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.Name Next ws MsgBox "已处理所有" & ThisWorkbook.Worksheets.Count & "个工作表" End Sub
Sub 遍历单元格区域() Dim cell As Range ' 定义单元格变量 ' 遍历A1:A10区域 For Each cell In Range("A1:A10") ' 如果单元格值大于50,标记为红色 If cell.Value > 50 Then cell.Interior.Color = RGB(255, 200, 200) End If Next cell End 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 Long For 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 Long For i = 1 To 3 For j = 1 To 4 Cells(i, j).Value = 销售表(i, j) Next j Next iEnd Sub
重要概念:二维数组就像Excel表格,第一个索引是行,第二个索引是列
04 效率革命:为什么数组比循环快100倍?
老王打开两个Excel文件:"左边这个用传统循环处理5万行数据,右边用数组处理。猜猜哪个快?"
传统方法:一个一个单元格处理
Sub 传统方法_慢速版() Dim startTime As Double startTime = Timer ' 记录开始时间 Dim lastRow As Long lastRow = Cells(Rows.Count, 1).End(xlUp).Row Dim i As Long For i = 2 To lastRow ' 每次操作都访问单元格,非常慢! Cells(i, 5).Value = Cells(i, 3).Value * Cells(i, 4).Value If Cells(i, 5).Value > 10000 Then Cells(i, 5).Value = Cells(i, 5).Value * 0.95 End If Next i MsgBox "处理完成!耗时:" & Round(Timer - startTime, 2) & "秒"End Sub
问题:每次循环都要读写Excel单元格,5万次就是5万次I/O操作,极慢!
数组方法:一次性读取,内存计算,一次性写入
Sub 数组方法_极速版() Dim startTime As Double startTime = Timer Application.ScreenUpdating = False ' 关闭屏幕刷新,提升速度 Dim lastRow As Long lastRow = Cells(Rows.Count, 1).End(xlUp).Row ' 1. 一次性把所有数据读到数组(瞬间完成) Dim arrData As Variant arrData = Range("A1:E" & lastRow).Value ' 2. 在内存中批量计算(不碰Excel,速度极快) Dim i As Long For 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 Then arrData(i, 5) = arrData(i, 5) * 0.95 End If Next i ' 3. 一次性写回工作表(瞬间完成) Range("A1:E" & lastRow).Value = arrData Application.ScreenUpdating = True ' 恢复屏幕刷新 MsgBox "处理完成!耗时:" & Round(Timer - startTime, 2) & "秒"End Sub
效率对比:
重要提醒:处理超过1000行数据时,一定要用数组!传统循环会让用户等到怀疑人生。
05 实战升级:批量处理月度销售报表
老王给出真实场景:"公司有12个月的销售数据,每个月一个工作表。需要:1)汇总全年数据;2)计算每月平均;3)找出最佳销售月。"
需求分析:
完整解决方案:
Sub 生成年度销售报告() ' 关闭屏幕刷新和自动计算,大幅提升速度 Application.ScreenUpdating = False Application.Calculation = xlCalculationManual Dim startTime As Double startTime = Timer ' 1. 准备汇总数组 Dim 月份名称(1 To 12) As String Dim 销售额(1 To 12) As Double Dim 月份 As Worksheet Dim 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. 遍历所有月份工作表 月份索引 = 1 For Each 月份 In ThisWorkbook.Worksheets ' 只处理月份工作表(名称包含"月") If InStr(月份.Name, "月") > 0 Then ' 一次性读取该月份的所有数据到数组 Dim 月份数据 As Variant 月份数据 = 月份.UsedRange.Value ' 计算该月份销售总额 Dim 行 As Long, 列 As Long Dim 本月销售额 As Double 本月销售额 = 0 ' 从第2行开始(跳过标题行) For 行 = 2 To UBound(月份数据, 1) ' 假设销售额在第3列 本月销售额 = 本月销售额 + 月份数据(行, 3) Next 行 ' 存储到汇总数组 销售额(月份索引) = 本月销售额 月份索引 = 月份索引 + 1 End If Next 月份 ' 3. 在"年度报告"工作表中生成汇总 Dim 报告 As Worksheet On Error Resume Next Set 报告 = ThisWorkbook.Worksheets("年度报告") On Error GoTo 0 If 报告 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 = xlCenter End With End With ' 4. 计算年度总额和每月占比 Dim 年度总额 As Double 年度总额 = 0 For i = 1 To 12 年度总额 = 年度总额 + 销售额(i) Next i ' 5. 写入月度数据并计算占比 Dim 最佳月份 As String Dim 最高销售额 As Double 最高销售额 = 0 For 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 If Next 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 = True Application.Calculation = xlCalculationAutomatic ' 显示结果 Dim 耗时 As Double 耗时 = Round(Timer - startTime, 2) MsgBox "年度报告生成完成!" & vbNewLine & _ "处理月份数:12个" & vbNewLine & _ "最佳销售月:" & 最佳月份 & vbNewLine & _ "年度总额:" & Format(年度总额, "#,##0.00") & vbNewLine & _ "处理耗时:" & 耗时 & "秒", _ vbInformation, "报告生成成功"End Sub
代码亮点解析:
- 数组存储:
- 批量读取:使用
UsedRange.Value一次性读取整个区域到数组 - 内存计算:
- 动态处理:
- 性能优化:
06 今日总结:从"单兵作战"到"集团军作战"
回顾今天学到的核心技能:
你今天的成长:
- 掌握了For循环的三种用法:正序、倒序、自定义步长
- 学会了用For Each优雅遍历工作表、单元格等集合
最重要的思维转变:
老王看着生成的年度报告,满意地点点头:"现在你写的代码,已经能'批量思考'了。昨天你教Excel如何思考,今天你教它如何高效思考。"
07 明日预告:错误处理与调试,让代码更健壮
小张运行年度报告宏时,突然弹出一个错误:"运行时错误'9':下标越界"。他一脸茫然:"这是什么意思?"
老王笑了:"这就是明天要学的——错误处理。让代码不仅快,还要稳。"
明日核心内容:
- 错误类型:
- 错误处理:
- 调试技巧:
- 实战:
今日作业:
优化今天的年度报告宏,让它能够:
提示:你会需要用到动态数组和更复杂的判断逻辑。明天我们揭晓答案!
最后的话:
真正的编程高手,不是写出能运行的代码,而是写出高效运行的代码。今天你学会了用循环和数组让代码效率提升百倍,这是从"能用"到"好用"的关键一步。
记住:处理数据时,内存 > 数组 > 循环 > 单元格操作。越靠近左边,效率越高。
明天,我们将让代码不仅快,还要稳如泰山。