23 KiB
23 KiB
Excel VBA 物控基础教程 📊
从零开始学习 Excel VBA 在物控中的应用
目录
VBA 基础语法
1. 变量声明
' 基本变量类型
Dim item_code As String ' 物料编码
Dim quantity As Double ' 数量
Dim unit_price As Currency ' 单价
Dim order_date As Date ' 订单日期
Dim is_critical As Boolean ' 是否关键物料
' 数组
Dim part_list(1 To 100) As String
Dim inventory_data(1 To 1000, 1 To 5) As Variant
' 集合和字典
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
2. 条件语句
' If-Then-Else
If quantity < safety_stock Then
MsgBox "库存不足!"
status = "需补货"
ElseIf quantity > max_stock Then
status = "库存过高"
Else
status = "正常"
End If
' Select Case(适合多条件判断)
Select Case abc_class
Case "A"
priority = "高"
review_freq = "每周"
Case "B"
priority = "中"
review_freq = "每月"
Case "C"
priority = "低"
review_freq = "每季度"
End Select
3. 循环结构
' For 循环(已知次数)
For i = 2 To last_row
part_no = Cells(i, "A").Value
qty = Cells(i, "B").Value
' 处理数据
If qty > 0 Then
Cells(i, "C").Value = qty * 1.1 ' 建议采购量
End If
Next i
' For Each 循环(遍历集合)
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Debug.Print ws.Name
Next ws
' Do While 循环(条件满足时继续)
Do While Cells(row, "A").Value <> ""
' 处理数据
row = row + 1
Loop
4. 函数定义
' 自定义函数(Function)
Function Calculate_Safety_Stock(avg_demand As Double, lead_time As Double) As Double
Dim safety_factor As Double
safety_factor = 1.5 ' 安全系数
Calculate_Safety_Stock = avg_demand * lead_time * safety_factor
End Function
' 子程序(Sub)
Sub Generate_Purchase_Order()
' 执行采购订单生成逻辑
MsgBox "采购订单生成完成!"
End Sub
常用对象
1. Worksheet 对象
' 引用工作表
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("库存")
Set ws = ThisWorkbook.Sheets(1) ' 按索引
' 激活工作表
ws.Activate
' 工作表属性
Debug.Print ws.Name ' 工作表名称
Debug.Print ws.Cells.Count ' 单元格数量
Debug.Print ws.UsedRange.Rows.Count ' 已使用行数
' 清空工作表
ws.Cells.Clear ' 清除所有内容和格式
ws.Cells.ClearContents ' 仅清除内容
2. Range 对象
' 单元格引用
Cells(1, 1).Value = "物料编码" ' 行列索引
Range("A1").Value = "物料编码" ' A1 表示法
Range("A1:C10").Value = "数据" ' 区域
' 动态范围
last_row = Cells(Rows.Count, "A").End(xlUp).Row
last_col = Cells(1, Columns.Count).End(xlToLeft).Column
' 批量操作
Range("A2:A" & last_row).Value = "默认值"
' 格式设置
With Range("A1:H1")
.Font.Bold = True
.Interior.Color = RGB(200, 200, 200)
.HorizontalAlignment = xlCenter
End With
3. Workbook 对象
' 当前工作簿
Set wb = ThisWorkbook
' 新建工作簿
Set new_wb = Workbooks.Add
' 打开工作簿
Set wb = Workbooks.Open("C:\路径\文件.xlsx")
' 保存
wb.Save
wb.SaveAs "C:\路径\新文件.xlsx"
' 关闭
wb.Close SaveChanges:=True
4. 文件对话框
' 选择文件
Dim file_path As String
With Application.FileDialog(msoFileDialogFilePicker)
.Title = "选择库存数据文件"
.Filters.Clear
.Filters.Add "Excel 文件", "*.xlsx;*.xls"
If .Show = -1 Then
file_path = .SelectedItems(1)
End If
End With
物控常用函数
1. 库存计算函数
' 计算安全库存
Function Safety_Stock(avg_demand As Double, lead_time As Double, _
Optional service_level As Double = 0.95) As Double
Dim z_score As Double
Select Case service_level
Case 0.90: z_score = 1.28
Case 0.95: z_score = 1.65
Case 0.98: z_score = 2.05
Case 0.99: z_score = 2.33
Case Else: z_score = 1.65
End Select
Safety_Stock = avg_demand * lead_time * z_score
End Function
' 计算订货点
Function Reorder_Point(avg_demand As Double, lead_time As Double, _
safety_stock As Double) As Double
Reorder_Point = avg_demand * lead_time + safety_stock
End Function
' 计算经济订货量(EOQ)
Function EOQ(annual_demand As Double, order_cost As Double, _
holding_cost As Double) As Double
EOQ = Sqr(2 * annual_demand * order_cost / holding_cost)
End Function
' 计算库存周转率
Function Inventory_Turnover(sales_cost As Double, avg_inventory As Double) As Double
If avg_inventory = 0 Then
Inventory_Turnover = 0
Else
Inventory_Turnover = sales_cost / avg_inventory
End If
End Function
' 计算库存天数
Function Inventory_Days(turnover As Double) As Double
If turnover = 0 Then
Inventory_Days = 0
Else
Inventory_Days = 365 / turnover
End If
End Function
2. 数据查找函数
' 查找物料信息
Function Get_Part_Info(part_no As String, info_type As String) As Variant
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("物料主数据")
Dim last_row As Long
last_row = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim i As Long
For i = 2 To last_row
If ws.Cells(i, "A").Value = part_no Then
Select Case info_type
Case "名称": Get_Part_Info = ws.Cells(i, "B").Value
Case "规格": Get_Part_Info = ws.Cells(i, "C").Value
Case "单位": Get_Part_Info = ws.Cells(i, "D").Value
Case "单价": Get_Part_Info = ws.Cells(i, "E").Value
Case "供应商": Get_Part_Info = ws.Cells(i, "F").Value
Case Else: Get_Part_Info = ""
End Select
Exit Function
End If
Next i
Get_Part_Info = "未找到"
End Function
' 查找库存数量
Function Get_Stock_Qty(part_no As String) As Double
On Error Resume Next
Get_Stock_Qty = Application.WorksheetFunction.VLookup( _
part_no, ThisWorkbook.Sheets("库存").Range("A:D"), 4, False)
If Err.Number <> 0 Then
Get_Stock_Qty = 0
Err.Clear
End If
On Error GoTo 0
End Function
3. 数据验证函数
' 检查物料编码格式
Function Validate_Part_No(part_no As String) As Boolean
' 假设物料编码格式:3位字母 + 6位数字(如:ABC123456)
If Len(part_no) = 9 Then
If Left(part_no, 3) Like "[A-Z][A-Z][A-Z]" And _
Right(part_no, 6) Like "######" Then
Validate_Part_No = True
Exit Function
End If
End If
Validate_Part_No = False
End Function
' 检查库存合理性
Function Validate_Stock_Qty(qty As Double, min_qty As Double, max_qty As Double) As Boolean
If qty >= min_qty And qty <= max_qty Then
Validate_Stock_Qty = True
Else
Validate_Stock_Qty = False
End If
End Function
实战案例
案例 1:库存报表自动生成
Sub Generate_Inventory_Report()
Dim ws_stock As Worksheet, ws_report As Worksheet
Dim last_row As Long, i As Long
Dim report_row As Long
' 设置工作表
Set ws_stock = ThisWorkbook.Sheets("库存")
' 删除旧报表
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Sheets("库存报表").Delete
Application.DisplayAlerts = True
On Error GoTo 0
' 创建新报表
Set ws_report = ThisWorkbook.Sheets.Add
ws_report.Name = "库存报表_" & Format(Date, "yyyy-mm-dd")
' 设置表头
ws_report.Range("A1:J1").Value = Array( _
"序号", "物料编码", "物料名称", "规格", "单位", _
"当前库存", "安全库存", "最高库存", "状态", "建议操作")
ws_report.Range("A1:J1").Font.Bold = True
' 获取数据
last_row = ws_stock.Cells(ws_stock.Rows.Count, "A").End(xlUp).Row
report_row = 2
For i = 2 To last_row
Dim part_no As String
Dim part_name As String
Dim spec As String
Dim unit As String
Dim current_qty As Double
Dim safety_qty As Double
Dim max_qty As Double
part_no = ws_stock.Cells(i, "A").Value
part_name = ws_stock.Cells(i, "B").Value
spec = ws_stock.Cells(i, "C").Value
unit = ws_stock.Cells(i, "G").Value
current_qty = ws_stock.Cells(i, "D").Value
safety_qty = ws_stock.Cells(i, "E").Value
max_qty = ws_stock.Cells(i, "F").Value
' 写入报表
ws_report.Cells(report_row, 1).Value = report_row - 1
ws_report.Cells(report_row, 2).Value = part_no
ws_report.Cells(report_row, 3).Value = part_name
ws_report.Cells(report_row, 4).Value = spec
ws_report.Cells(report_row, 5).Value = unit
ws_report.Cells(report_row, 6).Value = current_qty
ws_report.Cells(report_row, 7).Value = safety_qty
ws_report.Cells(report_row, 8).Value = max_qty
' 判断状态
If current_qty < safety_qty Then
ws_report.Cells(report_row, 9).Value = "⚠️ 库存不足"
ws_report.Cells(report_row, 9).Interior.Color = RGB(255, 200, 200)
ws_report.Cells(report_row, 10).Value = "立即补货"
ElseIf current_qty > max_qty Then
ws_report.Cells(report_row, 9).Value = "📦 库存过高"
ws_report.Cells(report_row, 9).Interior.Color = RGB(255, 255, 200)
ws_report.Cells(report_row, 10).Value = "暂停采购"
Else
ws_report.Cells(report_row, 9).Value = "✅ 正常"
ws_report.Cells(report_row, 9).Interior.Color = RGB(200, 255, 200)
ws_report.Cells(report_row, 10).Value = "维持现状"
End If
report_row = report_row + 1
Next i
' 格式化
ws_report.Columns("A:J").AutoFit
' 添加统计
ws_report.Range("L1").Value = "统计"
ws_report.Range("L2").Value = "总物料数:"
ws_report.Range("M2").Value = report_row - 2
ws_report.Range("L3").Value = "缺料数:"
ws_report.Range("M3").Value = Application.WorksheetFunction.CountIf(ws_report.Range("I:I"), "*不足*")
ws_report.Range("L4").Value = "库存过高数:"
ws_report.Range("M4").Value = Application.WorksheetFunction.CountIf(ws_report.Range("I:I"), "*过高*")
MsgBox "库存报表生成完成!" & vbCrLf & _
"报表名称:" & ws_report.Name
End Sub
案例 2:MRP 计算
Sub Calculate_MRP()
Dim ws_bom As Worksheet, ws_inventory As Worksheet, ws_mrp As Worksheet
Dim last_row As Long, i As Long, mrp_row As Long
' 设置工作表
Set ws_bom = ThisWorkbook.Sheets("BOM")
Set ws_inventory = ThisWorkbook.Sheets("库存")
' 删除旧 MRP 表
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Sheets("MRP").Delete
Application.DisplayAlerts = True
On Error GoTo 0
' 创建 MRP 表
Set ws_mrp = ThisWorkbook.Sheets.Add
ws_mrp.Name = "MRP"
' 设置表头
ws_mrp.Range("A1:H1").Value = Array( _
"物料编码", "物料名称", "需求量", "库存量", _
"净需求", "建议采购", "供应商", "到货日期")
ws_mrp.Range("A1:H1").Font.Bold = True
' 获取 BOM 数据
last_row = ws_bom.Cells(ws_bom.Rows.Count, "A").End(xlUp).Row
mrp_row = 2
For i = 2 To last_row
Dim part_no As String
Dim part_name As String
Dim qty_needed As Double
Dim qty_in_stock As Double
Dim net_qty As Double
Dim lead_time As Double
part_no = ws_bom.Cells(i, "A").Value
part_name = ws_bom.Cells(i, "B").Value
qty_needed = ws_bom.Cells(i, "C").Value
' 查找库存
On Error Resume Next
qty_in_stock = Application.WorksheetFunction.VLookup( _
part_no, ws_inventory.Range("A:D"), 4, False)
If Err.Number <> 0 Then
qty_in_stock = 0
Err.Clear
End If
On Error GoTo 0
' 计算净需求
net_qty = qty_needed - qty_in_stock
' 如果需要采购
If net_qty > 0 Then
ws_mrp.Cells(mrp_row, 1).Value = part_no
ws_mrp.Cells(mrp_row, 2).Value = part_name
ws_mrp.Cells(mrp_row, 3).Value = qty_needed
ws_mrp.Cells(mrp_row, 4).Value = qty_in_stock
ws_mrp.Cells(mrp_row, 5).Value = net_qty
' 建议采购量(考虑 MOQ 和安全系数)
Dim suggested_qty As Double
suggested_qty = net_qty * 1.1 ' 10% 安全系数
' 查找最小订货量
Dim moq As Double
On Error Resume Next
moq = Application.WorksheetFunction.VLookup( _
part_no, ws_inventory.Range("A:H"), 8, False)
If Err.Number <> 0 Then
moq = 1
Err.Clear
End If
On Error GoTo 0
If suggested_qty < moq Then
suggested_qty = moq
End If
ws_mrp.Cells(mrp_row, 6).Value = suggested_qty
' 查找供应商和提前期
On Error Resume Next
ws_mrp.Cells(mrp_row, 7).Value = Application.WorksheetFunction.VLookup( _
part_no, ws_inventory.Range("A:H"), 7, False)
lead_time = Application.WorksheetFunction.VLookup( _
part_no, ws_inventory.Range("A:H"), 6, False)
If Err.Number <> 0 Then
lead_time = 7 ' 默认 7 天
Err.Clear
End If
On Error GoTo 0
' 计算到货日期
ws_mrp.Cells(mrp_row, 8).Value = Date + lead_time
mrp_row = mrp_row + 1
End If
Next i
' 格式化
ws_mrp.Columns("A:H").AutoFit
' 添加汇总
ws_mrp.Range("J1").Value = "MRP 汇总"
ws_mrp.Range("J2").Value = "需采购物料数:"
ws_mrp.Range("K2").Value = mrp_row - 2
ws_mrp.Range("J3").Value = "总采购金额:"
ws_mrp.Range("K3").Formula = "=SUM(F2:F" & mrp_row - 1 & ")*VLOOKUP(A2,库存!A:E,5,FALSE)"
MsgBox "MRP 计算完成!" & vbCrLf & _
"需采购物料数:" & mrp_row - 2
End Sub
案例 3:呆滞料分析
Sub Analyze_Stagnant_Inventory()
Dim ws As Worksheet
Dim last_row As Long, i As Long
Dim stagnant_count As Long
Dim stagnant_value As Double
Set ws = ThisWorkbook.Sheets("库存")
last_row = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
' 添加分析列
ws.Cells(1, "I").Value = "呆滞状态"
ws.Cells(1, "J").Value = "呆滞天数"
ws.Cells(1, "K").Value = "呆滞金额"
' 清空旧数据
ws.Range("I2:K" & last_row).ClearContents
' 分析每行
For i = 2 To last_row
Dim last_move_date As Date
Dim days_stagnant As Long
Dim stock_qty As Double
Dim unit_price As Double
' 获取最后移动日期(假设在 H 列)
On Error Resume Next
last_move_date = ws.Cells(i, "H").Value
If Err.Number <> 0 Or last_move_date = 0 Then
last_move_date = Date - 365 ' 默认一年前
Err.Clear
End If
On Error GoTo 0
' 计算呆滞天数
days_stagnant = Date - last_move_date
ws.Cells(i, "J").Value = days_stagnant
' 计算呆滞金额
stock_qty = ws.Cells(i, "D").Value
unit_price = ws.Cells(i, "E").Value
ws.Cells(i, "K").Value = stock_qty * unit_price
' 判断呆滞状态
Select Case days_stagnant
Case Is > 365
ws.Cells(i, "I").Value = "🔴 严重呆滞(>1年)"
ws.Cells(i, "I").Interior.Color = RGB(255, 150, 150)
stagnant_count = stagnant_count + 1
stagnant_value = stagnant_value + stock_qty * unit_price
Case Is > 180
ws.Cells(i, "I").Value = "🟡 中度呆滞(6-12月)"
ws.Cells(i, "I").Interior.Color = RGB(255, 220, 150)
Case Is > 90
ws.Cells(i, "I").Value = "⚠️ 轻度呆滞(3-6月)"
ws.Cells(i, "I").Interior.Color = RGB(255, 255, 150)
Case Else
ws.Cells(i, "I").Value = "✅ 正常"
ws.Cells(i, "I").Interior.Color = RGB(200, 255, 200)
End Select
Next i
' 格式化
ws.Columns("I:K").AutoFit
' 统计结果
Dim total_value As Double
total_value = Application.WorksheetFunction.Sum(ws.Range("K2:K" & last_row))
MsgBox "呆滞料分析完成!" & vbCrLf & _
"总物料数:" & last_row - 1 & vbCrLf & _
"严重呆滞数:" & stagnant_count & vbCrLf & _
"呆滞料金额:$" & Format(stagnant_value, "#,##0.00") & vbCrLf & _
"呆滞比例:" & Format(stagnant_value / total_value * 100, "0.00") & "%"
End Sub
案例 4:盘点表生成
Sub Generate_Inventory_Count_Sheet()
Dim ws_stock As Worksheet, ws_count As Worksheet
Dim last_row As Long, i As Long
' 设置工作表
Set ws_stock = ThisWorkbook.Sheets("库存")
' 删除旧盘点表
On Error Resume Next
Application.DisplayAlerts = False
ThisWorkbook.Sheets("盘点表").Delete
Application.DisplayAlerts = True
On Error GoTo 0
' 创建新盘点表
Set ws_count = ThisWorkbook.Sheets.Add
ws_count.Name = "盘点表_" & Format(Date, "yyyy-mm-dd")
' 设置表头
ws_count.Range("A1:H1").Value = Array( _
"序号", "物料编码", "物料名称", "规格", _
"单位", "账面数量", "实盘数量", "差异")
ws_count.Range("A1:H1").Font.Bold = True
' 获取库存数据
last_row = ws_stock.Cells(ws_stock.Rows.Count, "A").End(xlUp).Row
For i = 2 To last_row
ws_count.Cells(i, 1).Value = i - 1
ws_count.Cells(i, 2).Value = ws_stock.Cells(i, "A").Value
ws_count.Cells(i, 3).Value = ws_stock.Cells(i, "B").Value
ws_count.Cells(i, 4).Value = ws_stock.Cells(i, "C").Value
ws_count.Cells(i, 5).Value = ws_stock.Cells(i, "G").Value
ws_count.Cells(i, 6).Value = ws_stock.Cells(i, "D").Value
Next i
' 设置差异公式
ws_count.Range("H2:H" & last_row).Formula = "=F2-G2"
' 格式化
ws_count.Columns("A:H").AutoFit
' 添加说明
ws_count.Range("J1").Value = "盘点说明"
ws_count.Range("J2").Value = "1. 在'实盘数量'列填写实际盘点数量"
ws_count.Range("J3").Value = "2. '差异'列会自动计算"
ws_count.Range("J4").Value = "3. 差异为负表示盘亏,为正表示盘盈"
' 添加统计
ws_count.Range("J6").Value = "盘点统计"
ws_count.Range("J7").Value = "总物料数:"
ws_count.Range("K7").Value = last_row - 1
MsgBox "盘点表生成完成!" & vbCrLf & _
"盘点表名称:" & ws_count.Name
End Sub
调试技巧
1. 基本调试方法
' 使用 Debug.Print 输出信息
Debug.Print "当前处理行:" & i
Debug.Print "物料编码:" & part_no
Debug.Print "库存数量:" & qty
' 使用断点
' 在代码左侧点击设置断点(F9)
' 程序会在断点处暂停,可以检查变量值
' 使用 Watch 窗口
' 视图 → 立即窗口 (Ctrl+G)
' 视图 → 监视窗口
2. 错误处理
' 基本错误处理
On Error Resume Next ' 忽略错误,继续执行
' 你的代码...
If Err.Number <> 0 Then
Debug.Print "错误:" & Err.Description
Err.Clear
End If
On Error GoTo 0 ' 恢复正常错误处理
' 详细错误处理
Sub Safe_Execution()
On Error GoTo ErrorHandler
' 主要代码
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("库存")
' 可能出错的操作
Dim qty As Double
qty = ws.Range("D100000").Value ' 可能超出范围
Exit Sub
ErrorHandler:
MsgBox "错误发生:" & vbCrLf & _
"错误号:" & Err.Number & vbCrLf & _
"错误描述:" & Err.Description & vbCrLf & _
"错误位置:" & Erl, vbCritical
Err.Clear
End Sub
3. 性能优化
' 优化前(慢)
Sub Slow_Code()
For i = 1 To 10000
Cells(i, "A").Value = Cells(i, "A").Value * 2 ' 每次都访问单元格
Next i
End Sub
' 优化后(快)
Sub Fast_Code()
Dim data_range As Range
Dim data_array As Variant
Dim i As Long
' 一次性读取数据到数组
Set data_range = Range("A1:A10000")
data_array = data_range.Value
' 在数组中处理
For i = 1 To 10000
data_array(i, 1) = data_array(i, 1) * 2
Next i
' 一次性写回
data_range.Value = data_array
End Sub
' 其他优化技巧
Sub Optimization_Tips()
Application.ScreenUpdating = False ' 关闭屏幕刷新
Application.Calculation = xlCalculationManual ' 关闭自动计算
Application.EnableEvents = False ' 关闭事件
' 执行耗时操作...
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
End Sub
4. 调试工具
' 立即窗口(Immediate Window)
' Ctrl+G 打开
' ? 变量名 ' 查看变量值
' ? 10 * 2 ' 计算表达式
' 本地窗口(Locals Window)
' 视图 → 本地窗口
' 显示所有局部变量的值
' 调试工具栏
' 视图 → 工具栏 → 调试
' 包含:继续、中断、逐语句、逐过程、跳出
快速参考
常用快捷键
| 快捷键 | 功能 |
|---|---|
| Alt + F11 | 打开 VBA 编辑器 |
| F5 | 运行宏 |
| F8 | 逐语句调试 |
| F9 | 设置/取消断点 |
| Ctrl + G | 打开立即窗口 |
| Ctrl + R | 打开工程资源管理器 |
| Ctrl + F | 查找 |
| Ctrl + H | 替换 |
常用对象模型
' 工作簿集合
Workbooks("文件名.xlsx")
' 工作表集合
ThisWorkbook.Sheets("表名")
ThisWorkbook.Sheets(1) ' 索引从 1 开始
' 单元格
Cells(行, 列) ' 如:Cells(1, 1) = A1
Range("A1") ' A1 单元格
Range("A1:C10") ' A1 到 C10 区域
' 行和列
Rows(1) ' 第 1 行
Columns("A") ' A 列
常用常量
' 对齐方式
xlLeft ' 左对齐
xlCenter ' 居中
xlRight ' 右对齐
' 边框样式
xlThin ' 细线
xlMedium ' 中等
xlThick ' 粗线
' 颜色
RGB(255, 0, 0) ' 红色
RGB(0, 255, 0) ' 绿色
RGB(255, 255, 0) ' 黄色
下一步
- 练习基础语法:尝试编写简单的宏
- 修改现有工具:根据你的需求调整代码
- 创建新工具:解决你的特定问题
- 学习高级技巧:数组、字典、正则表达式
- 分享你的代码:贡献到物控学习笔记
最后更新: 2026-02-03
版本: v1.0.0