I want to write a user defined function that wraps VLOOKUP. All it requires is a reference to the column that data should be imported from, and it will execute a VLOOKUP assuming that the IDs are in column A and there are fewer than 3000 rows to search.
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
I am running into two problems.
Firstly, the execution time is terrible. It takes several minutes to run this formula 1000 times, whereas the same VLOOKUP not wrapped in a UDF is very fast.
Secondly, when I first fill a column with =AutoVLookup(<column in other workbook>)
every row will incorrectly show the same result until something triggers them to recalculate.
What am I doing wrong?
edit, answer:
Here is the code I made using advice from Santosh and 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
It is much faster because it does not read in as much data, as it uses INDEX/MATCH rather than VLOOKUP. It also does not recalculate every time a cell in the sheet changes.
Try below code :
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
The main reasons your UDF is slow are:
1) you are forcing it to import 3000 rows of data from Excel to the VBA variant and then passing the 3000 rows of data back to VLOOKUP rather than just using a reference to the range
2) You are not bypassing the VBE Refresh bug
see the series of posts about building a faster lookup etc at
http://fastexcel.wordpress.com/2011/07/20/developing-faster-lookups-part-1-using-excels-functions-efficiently/
Also your UDF will not work correctly in circumstances where it references cells that are not included in the importfrom range.
Finally I am not sure I understand what you are trying to achieve: would it not be simpler (and much more efficient) to use INDEX or implicit referencing rather than VLOOKUP?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.