【问题标题】:VBA Is searching through cells faster with Find method or going through every cell?VBA 是使用 Find 方法更快地搜索单元格还是遍历每个单元格?
【发布时间】:2017-02-07 04:37:14
【问题描述】:

我在 Excel 中创建了一个 VBA 宏,用于查找所有工作表中特定函数的所有实例。我已经能够成功地创建它,但我正在尝试看看在性能方面最好的方法是什么,因为我正在搜索的功能可能会在大型工作簿中使用大量时间。

我使用了两种方法。

方法 1 - 遍历每个单独的单元格并使用“instr”函数查看单元格公式是否包含该函数。

方法 2 - 使用 Find 和 FindNext 方法以及 do 循环来仅循环遍历实际具有函数的单元格。

我惊讶地发现,当函数很多时,方法 1 快很多(当函数非常少时,方法 2 运行得更快)。

谁能解释一下这是怎么回事?

这是一个带有我的代码示例的示例。

在“Sheet1”上,我在单元格 A1:J5000 中放置了一个名为“MyFunction”的用户定义函数。然后在单元格 A5001:J10000 中,我将它们留空,但将它们着色为黄色以强制使用的范围为 A1:J10000。

尽管方法 1 循环遍历每 100,000 个单元格,但它比仅循环遍历找到的 50,000 个单元格的方法 2 快得多

方法 1 的平均运行时间约为 171 毫秒,方法 2 的平均运行时间约为 1,531 毫秒。

方法一和方法二的代码示例:

方法一

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

方法二

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

【问题讨论】:

标签: vba excel loops find


【解决方案1】:

让我们分解代码。这两个模块都在遍历每个单元格并测试以查看单元格中的公式。您只看到在您的方法 1 中,但 Excel 在评估对 .Find 的调用时也会检查目标 Range 中的每个单元格。

让我们计算每个循环中触及工作表的函数调用。方法 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 个参数......),它进行简单的检索和值字符串比较。

【讨论】:

  • 感谢您的回复,但是,我并不想获取使用次数。我在示例中仅将它们用于测试目的。我要做的是在工作簿中搜索函数的每个实例,然后对于函数所在的确切单元格,我将做其他事情。
  • 这样想......一个非常简单的版本会,对于我的函数所在的每个单元格,将其变为黄色。不是我在做什么,而是应该完成的逻辑类型的一个很好的例子。
  • 优秀。感谢您的洞察力。很有意义。我会研究更多并写回。谢谢!
猜你喜欢
  • 1970-01-01
  • 2017-05-14
  • 2013-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-07
  • 2018-08-15
  • 1970-01-01
相关资源
最近更新 更多