Performance Excel VBA-UDF包装VLOOKUP时行为奇怪且性能差
我想写一个用户定义的函数来包装VLOOKUP。它只需要引用应该从中导入数据的列,并且它将执行一个VLOOKUP,假设ID位于列a中,并且要搜索的行少于3000行Performance Excel VBA-UDF包装VLOOKUP时行为奇怪且性能差,performance,vba,excel,Performance,Vba,Excel,我想写一个用户定义的函数来包装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.
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()
填充一列时,每一行都会错误地显示相同的结果,直到有东西触发它们重新计算
我做错了什么
编辑,回答: 下面是我根据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
它的速度要快得多,因为它使用索引/匹配而不是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
请尝试以下代码:
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)您强制它将3000行数据从Excel导入VBA变量,然后将3000行数据传递回VLOOKUP,而不是仅仅使用范围的引用
2)您没有绕过VBE刷新错误
请参阅关于构建更快查找等的一系列文章在
您的UDF在引用不包含在importfrom范围内的单元格的情况下也无法正常工作。
最后,我不确定我是否理解您试图实现的目标:它会不会更简单(更有效)要使用索引或隐式引用而不是VLOOKUP?UDF速度慢的主要原因是:
1)您强制它将3000行数据从Excel导入VBA变量,然后将3000行数据传递回VLOOKUP,而不仅仅是使用范围的引用
2)您没有绕过VBE刷新bug
请参见
上关于构建快速查找等的一系列帖子。此外,如果UDF引用的单元格不包含在importfrom范围内,则UDF将无法正常工作。
最后,我不确定我是否理解您试图实现的目标:它是否会更简单(且效率更高)使用索引或隐式引用而不是VLOOKUP?快速注意:当您执行“Dim arg1Str,arg2Str As String”时,arg1Str被声明为变量,而不是字符串^^^真的吗?因此,变量类型仅适用于列表中的最后一个|它适用于与相邻的任何变量“As”(是的,最后一个)。@Tristan虽然变量被错误地声明为
Dim arg1、arg2、arg3、arg4变量
,而不是Dim arg1变量、arg2变量、arg3变量、arg4变量
,但幸运的是它们仍然是变量(bcoz未使用特定数据类型声明),这是函数所需的。请查看此链接是的,您需要声明所有内容。在同一行中,唯一可以忽略的是第二个变量及其后的“Dim”。快速注意:当您执行“Dim arg1Str,arg2Str As String”时arg1str被声明为变量,而不是字符串^^真的吗?因此变量类型仅适用于列表中的最后一个变量?:|它适用于与“as”相邻的任何变量(是的,在您的示例中是最后一个)@Tristan虽然变量被错误地声明为变量Dim arg1、arg2、arg3、arg4,而不是Dim arg1为变量、arg2为变量、arg3为变量、arg4为变量
,但幸运的是它们仍然是变量(bcoz未使用特定数据类型声明)这正是函数所需要的。查看此链接是的,你需要声明所有内容。在同一行中,唯一可以忽略的是“Dim”对于第二个变量和以后的变量。谢谢!这解决了问题1-Excel不再冻结。但是,我仍然有问题2-当我用这个公式填充1000行列时,它立即计算最后200,然后前800都显示相同的值几分钟。谢谢!这解决了问题1-Excel不再冻结.然而,我仍然有问题2-当我用这个公式填充1000行的列时,它会立即计算最后200行,然后前800行在几分钟内显示相同的值。