簡體   English   中英

Excel VBA-UDF包裝VLOOKUP時的行為異常且性能不佳

[英]Excel VBA - strange behaviour and poor performance when UDF wraps VLOOKUP

我想編寫一個包裝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。 每次工作表中的單元格更改時,它也不會重新計算。

試試下面的代碼:

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 Refresh錯誤

請參閱以下有關構建更快的查找的系列文章
http://fastexcel.wordpress.com/2011/07/20/developing-faster-lookups-part-1-using-excels-functions-efficiently/

同樣,您的UDF在引用importfrom范圍中未包含的單元格的情況下將無法正常工作。

最后,我不確定我是否理解您要實現的目標:使用INDEX或隱式引用而不是VLOOKUP會不會更簡單(效率更高)?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM