Excel VBA – 当UDF包装VLOOKUP时,奇怪的行为和糟糕的performance

我想编写一个包装VLOOKUP的用户定义函数。 它只需要引用数据应该被导入的列,并且假定ID在列A中并且有less于3000行要search,它将执行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的build议所做的代码:

 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。 每次单张表格中的单元格更改时,也不会重新计算。

尝试下面的代码:

 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 

你UDF速度慢的主要原因是:
1)您迫使它从Excel导入3000行数据到VBA变体,然后将3000行数据传回VLOOKUP,而不是仅使用范围的引用
2)你没有绕过VBE刷新错误

看到关于build立一个更快的查找等一系列的post
http://fastexcel.wordpress.com/2011/07/20/developing-faster-lookups-part-1-using-excels-functions-efficiently/

另外你的UDF在它引用的单元格不包含在importfrom范围内的情况下将不能正常工作。

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