我想编写一个包装VLOOKUP的用户定义函数。所需要的只是对应该从中导入数据的列的引用,并且它将执行VLOOKUP,假设ID在列A中并且要搜索的行少于3000行。
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。每次表格中的单元格发生变化时,它也不会重新计算。
答案 0 :(得分: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
答案 1 :(得分:2)
你的UDF速度慢的主要原因是:1)你强迫它从Excel导入3000行数据到VBA变体然后将3000行数据传回VLOOKUP而不是仅仅使用参考范围
2)您没有绕过VBE刷新错误
请参阅有关建立更快查找等的系列帖子http://fastexcel.wordpress.com/2011/07/20/developing-faster-lookups-part-1-using-excels-functions-efficiently/ >
在你引用未包含在importfrom范围内的单元格的情况下,你的UDF也无法正常工作。
最后我不确定我理解你想要实现的目标:使用INDEX或隐式引用而不是VLOOKUP并不简单(并且效率更高)?