VBA是使用Find方法更快地搜索单元格还是遍历每个单元格?

时间:2016-09-29 02:21:51

标签: excel vba excel-vba loops find

我在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

1 个答案:

答案 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个参数......)中进行了平滑处理,它可以进行简单的检索和值字符串比较。