Squeak.ru - шаблоны программирования

Excel VBA - странное поведение и низкая производительность, когда UDF обертывает VLOOKUP

Я хочу написать определяемую пользователем функцию, которая обертывает ВПР. Все, что ему требуется, — это ссылка на столбец, из которого должны быть импортированы данные, и он выполнит ВПР, предполагая, что идентификаторы находятся в столбце 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, выполняется очень быстро.

Во-вторых, когда я впервые заполняю столбец =AutoVLookup(<column in other workbook>), каждая строка будет неправильно показывать один и тот же результат, пока что-то не вызовет их пересчет.

Что я делаю не так?


отредактируйте, ответьте:

Вот код, который я сделал, используя советы Сантоша и Чарльза:

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

Это намного быстрее, потому что он не считывает столько данных, поскольку использует ИНДЕКС/ПОИСКПОЗ, а не ВПР. Он также не пересчитывается каждый раз, когда изменяется ячейка на листе.

23.05.2013

  • Краткое примечание: когда вы делаете Dim arg1Str, arg2Str As String arg1str объявляется как вариант, а не строка ^^ 23.05.2013
  • Действительно? Значит, тип переменной применяется только к последнему в списке? :| 23.05.2013
  • Это применимо к любой переменной As, с которой граничит (последняя в вашем случае). 23.05.2013
  • @Tristan Хотя переменные неправильно объявлены Dim arg1, arg2, arg3, arg4 As Variant вместо Dim arg1 As Variant, arg2 As Variant, arg3 As Variant, arg4 As Variant, но, к счастью, они все еще являются вариантами (bcoz не объявлены с определенным типом данных), и это то, что требует функция VLookup. Перейдите по этой ссылке msdn.microsoft. com/ru-ru/library/office/ 23.05.2013
  • Да, нужно декларировать все. Единственное, что вы можете опустить в той же строке, это Dim для второй переменной и далее. 23.05.2013

Ответы:


1

Попробуйте ниже код:

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
23.05.2013
  • Благодарю вас! Это решает проблему 1 — Excel больше не зависает. Однако у меня все еще есть проблема 2: когда я заполняю столбец из 1000 строк этой формулой, он сразу вычисляет последние 200, а затем первые 800 отображают одно и то же значение в течение нескольких минут. 23.05.2013

  • 2

    Основные причины, по которым ваш UDF работает медленно:
    1) вы заставляете его импортировать 3000 строк данных из Excel в вариант VBA, а затем передавать 3000 строк данных обратно в функцию ВПР, а не просто использовать ссылку на диапазон
    2) Вы не обходите ошибку обновления VBE

    см. серию сообщений о создании более быстрого поиска и т. д. на странице
    http://fastexcel.wordpress.com/2011

    Кроме того, ваша пользовательская функция не будет работать правильно в обстоятельствах, когда она ссылается на ячейки, не включенные в диапазон importfrom.
    < br> Наконец, я не уверен, что понимаю, чего вы пытаетесь достичь: не было бы проще (и гораздо эффективнее) использовать ИНДЕКС или неявные ссылки, а не ВПР?

    23.05.2013
    Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...