performance - 构建用于多键查找的快速 Excel VBA UDF

标签 performance vba excel excel-udf

我在一个工作簿中有几个相当大的数据表,需要创建一个 Excel 用户定义函数来查找这些表中的数据。我需要能够提供可变数量的键列和键值进行搜索,并且该函数需要找到第一个匹配行,然后使用此偏移量在相应的数据列中查找值。

有点像VLOOKUP的多键版本。我知道您可以创建一个由第 1 列中附加在一起的各个键组成的单个键,但我想避免这样做。

我已经创建了这个函数的几个版本,迄今为止最好的版本运行良好,只是!我的一个表有近 9,000 行 x 11 列,并且我使用 6 字段键。这个公式大约出现了 18,000 次,重新计算工作表大约需要 3 分钟(我计算出涉及 9.7 亿次计算,所以当然会很慢)。

它使用Evaluate(),就像我见过的许多解决方案一样。代码如下:

Function KeyLookup(datatable As Variant, datacol As String, _
                   key1table As Variant, key1 As String, _
                   Optional key2table As Variant, Optional key2 As String, _
                   Optional key3table As Variant, Optional key3 As String, _
                   Optional key4table As Variant, Optional key4 As String, _
                   Optional key5table As Variant, Optional key5 As String, _
                   Optional key6table As Variant, Optional key6 As String) As Variant
    Dim cmd As String

    cmd = "INDEX(" & datatable.Address & ",MATCH(1,("
    cmd = cmd & key1table.Address & "=""" & key1 & """)"
    If Not IsMissing(key2table) Then cmd = cmd & "*(" & key2table.Address & "=""" & key2 & """)"
    If Not IsMissing(key3table) Then cmd = cmd & "*(" & key3table.Address & "=""" & key3 & """)"
    If Not IsMissing(key4table) Then cmd = cmd & "*(" & key4table.Address & "=""" & key4 & """)"
    If Not IsMissing(key5table) Then cmd = cmd & "*(" & key5table.Address & "=""" & key5 & """)"
    If Not IsMissing(key6table) Then cmd = cmd & "*(" & key6table.Address & "=""" & key6 & """)"
    cmd = cmd & ",0)," & datacol & ")"

    KeyLookup = Evaluate(cmd)
End Function

这会生成如下所示的 cmd 值:

INDEX($K$3:$L$8993,MATCH(1,($B$3:$B$8993="a1-5")*($C$3:$C$8993="防水布")* ($E$3:$E$8993="悉尼")*($F$3:$F$8993="最高到达")*($G$3:$G$8993="1+")*($J$3:$ J$8993="T0"),1),1)

我需要一些帮助才能尽快完成此任务。 3 分钟太慢了。

如上所述,我希望避免基于 VLOOKUP() 的解决方案,因为我不想预先计算组合键。

我还想避免 SUMPRODUCT 解决方案仅适用于数字,并且不返回第一个值,而是在找到多个匹配项时对所有值求和。

我也不能依赖第三方加载项,尽管我知道存在一些不错的加载项。

所以,我目前的想法是本地使用 WorksheetFunction.Index()/Match() ,因此删除 Evaluate() 因为我知道这会增加大量的开销。

但是,我尝试删除 Evaluate() 时没有成功。有人可以在这里帮助我吗?

VBA 中的 WorksheetFunction.Index()/Match() 似乎只支持单个范围和单个键,除非有人可以解释如何实现那个可爱的 (range1=key1)*(range2=key2)... 表示法,工作表中的 MATCH 函数很受青睐,但 WorksheetFunction.Match() 不知何故不是。

最佳答案

非常好的帖子,虽然没有真正的问题。 ;) 编写代码时,像 IF 这样的条件检查会严重影响执行时间,但通常这是最可靠的检查方法。在上面的代码中,每次使用时,IF Not IsMissing 条件都会被检查5 次。这会导致您执行的每次检查的负载呈指数级增加(尽管我无法真正告诉您多少)。

无需过多编辑代码,即可应用一种逻辑一次跳过 5 项检查。不检查是否存在,而是检查不存在。基本上,您的公式有一个可选的key2。如果key2不存在,那么key3...key6也不存在。按照此模式,如果 key3 不存在,key4... key6 也不存在。

这给我们带来了直接的优势。当然,当你没有其他键时,用一个变量检查​​代替五个变量检查​​是一个很大的飞跃。但是,如果您一次将其用于 6 个完整键,我将研究完全不同的代码。 Evaluate 是一个巨大的 killer ,如果您是那种每次都让 UDF 重新计算的类型(即 Application.Volatile),您的计算时间将花费更多巨大的打击。

为了显示非常的微小更改,以下是我对您的代码的看法(未经测试):

Function KeyLookup(datatable As Variant, datacol As String, _
                   key1table As Variant, key1 As String, _
                   Optional key2table As Variant, Optional key2 As String, _
                   Optional key3table As Variant, Optional key3 As String, _
                   Optional key4table As Variant, Optional key4 As String, _
                   Optional key5table As Variant, Optional key5 As String, _
                   Optional key6table As Variant, Optional key6 As String) As Variant
    Dim cmd As String

    cmd = "INDEX(" & datatable.Address & ",MATCH(1,(" & key1table.Address & "=""" & key1 & """)"
    cmd2 = ",0)," & datacol & ")"

    If IsMissing(key2table) Then GoTo SkipOthers
    ElseIf IsMissing(key3table) Then
        cmd = cmd & "*(" & key2table.Address & "=""" & key2 & """)"
        GoTo SkipOthers
    ElseIf IsMissing(key4table) Then
        cmd = cmd & "*(" & key3table.Address & "=""" & key3 & """)"
        GoTo SkipOthers
    ElseIf IsMissing(key5table) Then
        cmd = cmd & "*(" & key4table.Address & "=""" & key4 & """)"
        GoTo SkipOthers
    ElseIf IsMissing(key6table) Then
        cmd = cmd & "*(" & key5table.Address & "=""" & key5 & """)"
        GoTo SkipOthers
    Else
        cmd = cmd & "*(" & key6table.Address & "=""" & key6 & """)"
    End If

SkipOthers:
    KeyLookup = Evaluate(cmd & cmd2)

End Function

如果您想继续走这条路,祝您好运。 :)

关于performance - 构建用于多键查找的快速 Excel VBA UDF,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19943027/

相关文章:

python - N gram NLP 到 Excel 文件

Excel 到 PDF 多个范围

c# - 在 C# 中将二维数组归零的最快方法

vba - 从一个Word文档中选择一系列文本,然后复制到另一个Word文档中

performance - 从 ghci 和 shell 运行的已编译加速代码的性能差异

python - 如何将空可选参数从 Python 传递到 Excel VBA 宏

excel - IfError 未捕获类型类型不匹配错误 vba

vba - 如何将工作表 A 保存到文件夹 A 并将工作表 B 保存到文件夹 B?

c++ - arduino 低 i2c 读取速度;

java - Java 中的 toString 覆盖