我在Excel中创建了一个VBA宏,用于查找所有工作表中特定功能的所有实例。我已经能够成功创建它,但我正在尝试看看性能最好的方法是什么,因为我正在搜索的功能可能会大量使用大型工作簿。
我使用了两种方法。
方法1 - 循环遍历每个单独的单元格并使用“instr”函数查看单元格公式是否包含该函数。
方法2 - 使用Find和FindNext方法以及do循环仅循环实际具有函数的单元格。
我很惊讶地发现当有大量函数时方法1的速度要快得多(当方法2的工作速度很快时)。
任何人都可以解释这是怎么回事吗?
以下是我的代码示例示例。
在“Sheet1”上,我在单元格A1:J5000中放置了一个名为“MyFunction”的用户定义函数。然后在单元格A5001:J10000中我将它们留空,但是将它们染成黄色以强制使用的范围为A1:J10000。
即使方法1循环每100,000个单元格,它也比方法2快得多,方法2只循环通过找到的50,000个单元格
方法1平均运行时间约为171毫秒,方法2平均运行时间约为1,531毫秒。
方法1和方法2的代码示例:
方法1
Private Sub TestMethod1()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim MySheet As Worksheet, MyRange As Range, MyCell As Range
Dim MyCellAddress As String, MyCellFormula As String, MyFunction As String
Dim CountTotalCells As Long, CountTotalFunctions As Long
Dim sw, swEndTime As Long
Set sw = New StopWatch
sw.StartTimer
MyFunction = "=MyFunction("
CountTotalCells = 0
CountTotalFunctions = 0
Set MySheet = Sheets("Forum Question")
Set MyRange = MySheet.UsedRange
For Each MyCell In MyRange
MyCellFormula = MyCell.Formula
CountTotalCells = CountTotalCells + 1
If InStr(1, MyCellFormula, MyFunction) > 0 Then
CountTotalFunctions = CountTotalFunctions + 1
End If
Next
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
swEndTime = sw.EndTimer
MsgBox CountTotalCells & ", " & CountTotalFunctions & ", " & swEndTime & " ms"
End Sub
方法2
Private Sub TestMethod2()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim MySheet As Worksheet, MyRange As Range, MyCell As Range
Dim MyCellAddress As String, MyCellFormula As String, MyFunction As String, MyCellFirst As String
Dim CountTotalCells As Long, CountTotalFunctions As Long
Dim sw, swEndTime As Long
Set sw = New StopWatch
sw.StartTimer
MyFunction = "=MyFunction("
CountTotalCells = 0
CountTotalFunctions = 0
Set MySheet = Sheets("Forum Question")
Set MyRange = MySheet.UsedRange
Set MyCell = MyRange.Cells.Find( _
What:=MyFunction, _
After:=[A1], _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlRows, _
SearchDirection:=xlNext, _
MatchCase:=True _
)
If Not MyCell Is Nothing Then
MyCellFirst = MyCell.Address
Do
Set MyCell = MyRange.FindNext(After:=MyCell)
MyCellAddress = MyCell.Address
MyCellFormula = "z" & MyCell.Formula
CountTotalCells = CountTotalCells + 1
If InStr(1, MyCellFormula, MyFunction) > 0 Then
CountTotalFunctions = CountTotalFunctions + 1
End If
If MyCell Is Nothing Or MyCellAddress = MyCellFirst Then
Exit Do
End If
Loop
End If
Set MyCell = Nothing
swEndTime = sw.EndTimer
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
MsgBox CountTotalCells & ", " & CountTotalFunctions & ", " & swEndTime & " ms"
End Sub
答案 0 :(得分:1)
让我们分解代码。两个模块都循环遍历每个单元格并测试以查看单元格中的公式。您只在方法1中 查看 ,但Excel在评估对Range
的调用时也会检查目标.Find
中的每个单元格。
让我们计算在每个循环中触摸Worksheet的函数调用。方法1恰好有1:
MyCell.Formula
方法2具有以下内容:
MyRange.FindNext
MyCell.Address
MyCell.Formula
......加上这些比较......
MyCell Is Nothing
MyCellAddress = MyCellFirst
...加上这个字符串连接:
MyCellFormula = "z" & MyCell.Formula
所以让我们加上伤害。我冒昧地添加了性能分析代码来测试每个行所花费的总时间(使用更大的样本或单元格):
Set MyCell = MyRange.Cells.Find: 0 seconds
MyCellFirst = MyCell.Address: 0.421875 seconds
Set MyCell = MyRange.FindNext(After:=MyCell): 4.3125 seconds
MyCellFormula = "z" & MyCell.Formula: 0.34375 seconds
If MyCell Is Nothing Or MyCellAddress = MyCellFirst Then Exit Do: 0.015625 seconds
所以,性能最高的是.FindNext
,这并不奇怪。它在内部进行了大量的工作,在方法1(仅仅评估7个参数......)中进行了平滑处理,它可以进行简单的检索和值字符串比较。