如何使用数组而不是在VBA中查找来提高效率

时间:2016-09-17 00:58:45

标签: arrays vba

我有一个用于在Excel工作表中查找信息的函数,知道: - 密钥可以在变量列中 - 可以搜索变量字段 表格通常少于一百列,但可以搜索几百到十万行。在我们最大的文件中,我尝试优化的功能可以使用大约一百万次。

看完之后 https://fastexcel.wordpress.com/2011/10/26/match-vs-find-vs-variant-array-vba-performance-shootout/

...并且发现我们的函数使用Find(3次),我尝试使用数组。

这是我写的代码

Function getInfo(Key As String, NameField As String, NameKey As String, WksName As String) As Variant

On Error GoTo Error

Dim iColumnKEY As Integer
Dim iColumnFIELD As Integer
Dim i As Integer
Dim ListFields, ListKeys As Variant

ListFields = Worksheets(WksName).Range("A1:ZZ1")


i = LBound(ListFields, 2)

'To identify which column contains the Key and which one contains the  
'information we are searching for
Do While iColumnKEY=0 Or iColumnFIELD=0
    If i > UBound(ListFields, 2) Then
        getInfo = "//error\\"

    ElseIf ListFields(1, i) = NameKey Then
        iColumnKEY = i
    ElseIf ListFields(1, i) = NameField Then
        iColumnFIELD = i
    End If
i = i + 1
Loop

Dim iROW As Integer

ListKeys = Worksheets(WksName).Columns(iColumnFIELD)

i = LBound(ListKeys, 1)
Do While iROW=0
    If i > UBound(ListKeys,1) Then
        getInfo = "//error\\"

    ElseIf ListKeys(i,1) = Key Then
        iROW = i
    End If
    i = i + 1
 Loop

 getInfo = Worksheets(WksName).Cells(iROW, iColumnFIELD)

Exit Function

Error:
    getInfo = "//error\\"

End Function

代码有效,但速度很慢。我在做什么让事情变得缓慢?

现在不在代码中,但我确实尝试关闭屏幕更新,以及自动计算。我没有看到速度上的任何差异,这表明基本算法是主要问题。

此外,文章是在2011年。数组是否仍然比匹配/查找快得多?

作为旁注:最后,我建议使用一个宏来批量搜索一系列密钥,而不是为每个密钥调用该函数。这意味着第一个Do ... While循环只对宏执行一次,并且只为每个键运行Do_While for Rows。但是,这在短期内不是一种选择。

感谢。任何帮助或建议将不胜感激。

2 个答案:

答案 0 :(得分:0)

要了解代码的哪些部分最慢,可以使用Timer

Dim t as Single
t = Timer
' part of the code
Debug.Print CDbl(Timer - t) ' CDbl to avoid scientific notation

使用.Value2代替.Value应该有所帮助:

ListFields = Worksheets(WksName).Range("A1:ZZ1").Value2

在两个单独的循环中搜索键和字段应该更快一些,因为会有较少的比较。此外,我不确定它是否会更慢或更快,但你甚至可以迭代多维数组:

Dim i As Integer, v ' As Variant
i = 1

For Each v in ListFields 
    If v = NameKey Then
        iColumnKEY = i
        Exit For
    End If
    i = i + 1
Next

答案 1 :(得分:0)

在您的代码中,您永远不会使用iColumnKEY

我认为这就是你真正追求的目标:

Function getInfo(key As String, NameField As String, NameKey As String, WksName As String) As Variant

    Dim keyCol As Variant, fieldCol As Variant, keyRow As Variant
    Dim errMsg As String

    getInfo = "//error\\"

    With Worksheets(WksName)
        With Intersect(.UsedRange, .Columns("A:ZZ")) ' <--| reference a range in passed worksheet cells belonging to columns "A" to "ZZ" from worksheet first used row to last used one and from worksheet first used column to last used one
            MsgBox .Address
            fieldCol = Application.Match(NameField, .Rows(1), 0) '<--| look for passed 'NameField' in referenced range
            If IsError(fieldCol) Then
                errMsg = " :field column '" & NameField & "' not found"
            Else
                keyCol = Application.Match(NameKey, .Rows(1), 0) '<--| look for passed 'NameKey' in referenced range
                If IsError(keyCol) Then
                    errMsg = " :key column '" & NameKey & "' not found"
                Else
                    MsgBox .Columns(keyCol).Address
                    keyRow = Application.Match(key, .Columns(keyCol)) '<--| look for passed 'key' in referenced range 'NameKey' column
                    If IsError(keyRow) Then
                        errMsg = " :key '" & key & "' not found in column '" & NameKey & "'"
                    Else
                        getInfo = .Cells(keyRow, fieldCol) '<--| get referenced range "item"
                    End If
                End If
            End If
            If errMsg <> "" Then getInfo = getInfo & errMsg
        End With
    End With
End Function