0

我想编写一个包装 VLOOKUP 的用户定义函数。它所需要的只是对应该从中导入数据的列的引用,并且假设 ID 在 A 列中并且要搜索的行少于 3000 行,它将执行 VLOOKUP。

Function AutoVlookup( importFrom As Range) As Variant
    Dim arg1, arg2, arg3, arg4 As Variant
    Dim arg1Str, arg2Str As String

    arg1Str = "$A" & Application.Caller.row 'get ID
    arg1 = Application.Caller.Parent.Range(arg1Str)
    arg2Str = "$A$1:$" & Split(cells(1, importFrom.column).Address, "$")(1) & "$3000"
    arg2 = importFrom.Parent.Range(arg2Str) 'get range to search in (in other workbook)
    arg3 = importFrom.column 'get column to return
    arg4 = False 'exact match

    AutoVlookup = Application.WorksheetFunction.VLookup(arg1, arg2, arg3, arg4)   
End Function

我遇到了两个问题。

首先,执行时间很糟糕。运行此公式 1000 次需要几分钟,而未包装在 UDF 中的相同 VLOOKUP 非常快。

其次,当我第一次用每一行填充一列时,=AutoVLookup(<column in other workbook>)会错误地显示相同的结果,直到有东西触发它们重新计算。

我究竟做错了什么?


编辑,回答:

这是我使用 Santosh 和 Charles 的建议编写的代码:

Function EasyLookup(importFrom As Range) As Variant
    Application.Volatile False 'does not recalculate whenever cells on sheet change

    Dim Id As String
    Dim match As Integer
    Dim importColumnAddress As String
    Dim initialCalculationSetting As XlCalculation
    Dim initialScreenUpdateMode As Boolean
    Dim initialEnableEventsMode As Boolean

    'saving the settings, to be reverted later
    initialScreenUpdateMode = Application.ScreenUpdating
    initialCalculationSetting = Application.Calculation
    initialEnableEventsMode = Application.EnableEvents
    'changes screen update and calculation settings for performance
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Application.EnableEvents = False

    'find ID on formula's sheet
    Id = Application.caller.Parent.Cells(Application.caller.row, 1).value
    'find row with ID on column A of data source sheet
    match = Application.WorksheetFunction.match(Id, importFrom.Parent.Range("$A$1:$A$4000"), 0) 'assumes no more than 4000 rows.

    'retrieve value from importFrom's column, on the row where ID was found
    importColumnAddress = Split(Cells(1, importFrom.column).Address, "$")(1)
    importColumnAddress = importColumnAddress & ":" & importColumnAddress
    EasyLookup = Application.WorksheetFunction.Index(importFrom.Parent.Range(importColumnAddress), match)

    'revert performance tweaks
    Application.ScreenUpdating = initialScreenUpdateMode
    Application.Calculation = initialCalculationSetting
    Application.EnableEvents = initialEnableEventsMode
End Function

它的速度要快得多,因为它不会读入尽可能多的数据,因为它使用 INDEX/MATCH 而不是 VLOOKUP。每次工作表中的单元格更改时,它也不会重新计算。

4

2 回答 2

2

试试下面的代码:

Function AutoVlookup(importFrom As Range) As Variant

    Application.Volatile False
    Application.Calculation = xlCalculationManual
    Application.ScreenUpdating = False
    Application.EnableEvents = False

    Dim arg1, arg2, arg3, arg4 As Variant
    Dim arg1Str, arg2Str As String
    Dim rng As Object

    Set rng = Application.Caller
    arg1Str = "$A" & rng.Row    'get ID
    Set arg1 = Application.Caller.Parent.Range(arg1Str)

    arg2Str = "$A$1:$" & Split(Cells(1, importFrom.Column).Address, "$")(1) & "$3000"
    Set arg2 = importFrom.Parent.Range(arg2Str)    'get range to search in (in other workbook)

    arg3 = importFrom.Column    'get column to return
    arg4 = False    'exact match

    AutoVlookup = Application.VLookup(arg1, arg2, arg3, arg4)

    Application.ScreenUpdating = True
    Application.Calculation = xlCalculationAutomatic
    Application.EnableEvents = True
End Function
于 2013-05-23T05:24:36.463 回答
2

您的 UDF 速度慢的主要原因是:
1)您强制它将 3000 行数据从 Excel 导入 VBA 变体,然后将 3000 行数据传递回 VLOOKUP 而不仅仅是使用对范围的引用
2)您没有绕过 VBE 刷新错误,请参阅http://fastexcel.wordpress.com/2011/07/20/developing-faster-lookups-part-1-using-excels-functions

上有关构建更快查找等的一系列帖子- 有效
/

此外,您的 UDF 在引用未包含在 importfrom 范围内的单元格的情况下将无法正常工作。

最后,我不确定我是否理解您要实现的目标:使用 INDEX 或隐式引用而不是 VLOOKUP 会不会更简单(而且效率更高)?

于 2013-05-23T07:33:55.573 回答