我有一个用于在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。但是,这在短期内不是一种选择。
感谢。任何帮助或建议将不胜感激。
答案 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