3

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

有点像VLOOKUP. 我知道您可以创建一个密钥,由附加在第 1 列中的各个密钥组成,但我想避免这样做。

我已经创建了这个函数的几个版本,到目前为止最好的一个可以正常工作,只是速度很!我的一张表是近 9,000 行乘 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="Tarp")*($E$3:$E$8993="Sydney")*($F$3:$F$8993="Highest Reach")*($G$3:$G$8993="1+")*($J$3:$J$8993="T0"),1),1)

我需要一些帮助才能尽快完成这项工作。3分钟太慢了。

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

我还想避免SUMPRODUCT只使用数字并且不返回第一个值但如果找到多个匹配项则对所有值求和的解决方案。

我也不能依赖第三方加载项,即使我知道存在一些好的加载项。

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

但是,我尝试删除Evaluate(). 有人可以在这里帮助我吗?

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

4

2 回答 2

0

查看 .NET(或替代方法)将大部分代码重组为外部组件。更改 UDF 以简单地将范围转换为二维对象数组并传递它。使用返回值作为传递回 UDF 函数的返回调用的值。

其他任何事情都不会显着减少处理时间。

这是进程内计算引擎的限制。它适用于纯数学,但使用 excel 对象或其函数(除了原语)往往会受到巨大打击。甚至还有一篇关于 Excel UDF 限制的 kb 文章。值得一读。

Excel中自定义函数的限制说明

于 2015-02-11T03:13:41.737 回答
0

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

无需过多编辑代码,可以应用一种逻辑一次跳过 5 次检查。与其检查存在,不如检查不存在。基本上,您的公式有一个可选的key2. 如果key2不存在,那么也不存在key3... key6。遵循这种模式,如果key3不存在,key4... key6也不会。

这给了我们一个直接的优势。当然,当您没有其他键时,变量检查而不是五个检查是一个很大的飞跃。但是,如果您一次使用 6 个完整键,我将研究完全不同的代码。Evaluate是一个巨大的杀手,如果你是那种让你的 UDFApplication.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

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

于 2013-11-13T03:42:36.590 回答