Страницы: 1
RSS
Замена(ускорение) с помощью VBA формулы ВПР
 
Добрый день. Есть 2 файла с 300 000+ строк и 30+ столбцов. Из одного файла в другой надо заВПРить более 15 столбцов по 1 столбцу с ключом. Есть макрос, в котором прописаны именно ВПР типа
Код
Set x = Workbooks(xFile).Worksheets(xsheet).Rows(1).find("Поставщик", , xlFormulas, xlWhole)
   x1 = x.Column
 Cells(2, x1).FormulaR1C1 = "=VLOOKUP(C1,'[" & "Расчет_общий.xlsb" & "]" & xsheets & "'!C1:C8,8,0)" 
...
Application.Calculation = xlCalculationManual
Range("H2:S2").AutoFill Destination:=Range("H2:S" & xlastrow)
Application.Calculation = xlCalculationAutomatic
Range("H2:S" & xlastrow).Value = Range("H2:S" & xlastrow).Value

Таким способом макрос работает около часа. При отключении автоматического пересчета формул и включением после протягивания формул, макрос работает около 40 минут. Пробовал делать это циклом типа

Код
For i=1 to lastrow  
For i1=1 to lastrow2   
If cells(i,1)=cells(i1,1) then   
cells(i,10)=cells(i1,18) and _   
cells(i,12)=cells(i1,22) and _   
....   
End if  
Next i1
Next i

Работает такой вариант намного дольше (примерно 90 минут). Пробовал через массивы, в которые я закидывал обе таблицы + цикл типа предыдущего. Работает такой вариант тоже дольше чем ВПР.

Подскажите, пожалуйста, вариант, который будет работать быстрее. Если возможно, то небольшой пример кода с пояснениями.

 
Hellmaster Используйте массивы.
 
Hellmaster, как минимум ускорить можно в 15 раз, используя сперва один раз поиск строки, а потом уже сослаться на него.
Не стесняйтесь приложить пример.
Изменено: БМВ - 17.01.2020 14:23:17
По вопросам из тем форума, личку не читаю.
 
Код
Sub Main()
    Dim sh1 As Worksheet: Set sh1 = Workbooks("Ðàñ÷åò_îáùèé.xlsb").Sheets(1)
    Dim sh2 As Worksheet: Set sh2 = ActiveSheet
    
    Dim dicY As Object
    Set dicY = FillDic(sh1)
    
    
    Cells(1, 2).Value = NewVPR(Cells(1, 1).Value, sh1, dicY, 2)
End Sub

Function FillDic(sh As Worksheet) As Object
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    
    Dim a As Variant
    With sh
        a = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp))
    End With
    
    Dim y As Long
    For y = 1 To UBound(a, 1)
        dic.Item(a(y, 1)) = y
    Next
    Set FillDic = dic
End Function

Function NewVPR(v As Variant, sh As Worksheet, dic As Object, x As Integer) As Variant
    If dic.Exists(v) Then
        Dim y As Integer
        y = dic.Item(v)
        NewVPR = sh.Cells(y, x).Value
    Else
        NewVPR = CVErr(xlErrNA)
    End If
End Function
Может так будет быстрее.
 
Hellmaster, здравствуйте!
В первую очередь, если вы тянете несколько столбцов из одной и той же строки, то сначала =ПОИСКПОЗ() ~ 95% времени, а потом =ИНДЕКС() для каждого столбца — ~ 5% времени. Именно об этом вам говорит БМВ

Далее нужно смотреть и сравнивать по скорости - это будет либо решение на словарях (скорее всего), либо (если уникальных ключей больше 100 тысяч), то сортировка массива с последующим бинарным поиском ключей по нему

В любом случае, стоит отказаться от макрофункции листа в пользу процедуры обработки внутри кода и вставки полученных значений на лист — ускорение тем более явное, чем больше вызовов функций  :idea:

В любом случае, прирост будет в десятки и сотни раз  ;)
Ссылка на мою тему с похожими задачами
Изменено: Jack Famous - 17.01.2020 14:52:33
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
БМВ,спасибо. Использовал формулу индекс(поискпоз. сработало быстрее, но не намного.
МатросНаЗебре, спасибо. Проверю- отпишусь в эту тему.
Jack Famous, спасибо. Насколько я понял, вы предлагаете создать отдельный столбец с =поискпоз(), а дальше работать со словарем? Сортировки в обоих массивах сделаны до использования формул для меньшего кол-ва вхождений.  
 
Цитата
Hellmaster: вы предлагаете создать отдельный столбец с =поискпоз()
верно
Цитата
Hellmaster: а дальше работать со словарем?
хотя бы просто тянуть ИНДЕКСом со ссылкой на найденную позицию
Цитата
Hellmaster: Сортировки в обоих массивах сделаны до использования формул для меньшего кол-ва вхождений
я не вижу, что у вас там сделано, но на отсортированном массиве можно использовать дополнительные аргументы функций ВПР или ПОИСКПОЗ - тогда поиск будет практически мгновенным. То же справедливо и для VBA, но там дополнительно можно увеличить скорость за счёт грамотной логики
Изменено: Jack Famous - 17.01.2020 16:20:05
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
МатросНаЗебре, у меня в словарь добавляется 256 строк, при этом циклом проходит все строки массива а (7000 строк). Как расширить словарь, чтобы в него поместились все ключи (7000 строк)?
Код
a = sh1.UsedRange.Value
    Set sd = CreateObject("Scripting.Dictionary")
    For i = 1 To UBound(a)
      Key = a(i, 1)
      Item = a(i, 7) 
      sd.Add Key, Item 
    Next
Изменено: Hellmaster - 22.01.2020 13:52:55
 
Цитата
Hellmaster: Как расширить словарь
при добавлении ключей словарь "расширяется" самостоятельно (матчасть)
Если ваш код вызывает ошибку, то только потому, что у вас дубли в ключах - можно обойти так:
Код
Option Explicit
Sub Test()
Dim dic as Object, arr, i&

arr = sh1.UsedRange.Value2
Set dic = CreateObject("Scripting.Dictionary")

    For i = 1 To UBound(arr)
        dic(arr(i, 1))=arr(i, 7) 
    Next i
End Sub
Изменено: Jack Famous - 22.01.2020 13:56:39
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Jack Famous, да, уже проштудировал этот и многие другие сайты. Но у меня в словарь добавляется максимум 256 item.
Соответственно, когда тяну из словаря данные, тянется только 256 значений.
Ошибку не вызывает, дублей нет. У меня просто в словарь добавляется 256 item вместо нужных 7000. Объясните, пожалуйста, что я не так делаю
Изменено: Hellmaster - 22.01.2020 13:58:02
 
Цитата
Hellmaster: уже проштудировал
читайте внимательнее и проверяйте, где именно у вас ошибка… (обновил пост выше)
Цитата
Hellmaster: У меня просто в словарь добавляется 256 item вместо нужных 7000
количество элементов словаря зависит от количества уникальных ключей. Все 7000 ключей — уникальны??? Пример давайте
Изменено: Jack Famous - 22.01.2020 14:06:19
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Hellmaster написал:
чтобы в него поместились все ключи
Как минимум убедиться, что все значения из 7000 уникальные.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Пример во вложении. Так же прилагаю пару скриншотов. Столбец1 - столбец с ключом.
 
Я так понимаю, это утверждение, что в словаре не более 256 элементов.
Попробуйте:
Код
Debug.Print sd.Count
Во удивитесь )
 
МатросНаЗебре, в том то и дело, что count правильный, а item только 256. Соответственно, в итоговую таблицу подтягивает только 256 значений из словаря, а не 7000, как делает впр.
 
МатросНаЗебре, и так видно по скрину "каунт", что там всё есть
Hellmaster, У меня в примере всё нормально занеслось
Изменено: Jack Famous - 22.01.2020 14:21:11
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Hellmaster написал:
а item только 256
окно Debug не может отображать более 256 элементов. Поэтому и показывает только сколько может, но это не значит, что их столько.
И что в приложенном файле надо сделать, чтобы увидеть проблему? Я что-то не нашел там ни намека на код со словарями...
Изменено: Дмитрий(The_Prist) Щербаков - 22.01.2020 14:21:16
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Дмитрий(The_Prist) Щербаков, вот мой код полностью.
Код
Sub NewVPR()

    Dim a()
    Dim i&
    Dim sd As Object
    Dim sh1 As Worksheet: Set sh1 = Workbooks("Пример.xlsb").Sheets(1)
    Dim sh2 As Worksheet: Set sh2 = ActiveSheet

    a = sh1.UsedRange.Value
    Set sd = CreateObject("Scripting.Dictionary")
    For i = 1 To UBound(a)
      Key = a(i, 1) ' получаем ключ из первого столбца текущей строки массива
      Item = a(i, 7) ' получаем элемент из седьмого столбца текущей строки массива
      sd.Add Key, Item ' и добавляем новый элемент в коллекцию
    Next
    a = sh2.UsedRange.Value
    For i = 1 To UBound(a)
        If sd.Exists(a(i, 1)) Then a(i, 8) = sd.Item(a(i, 1))

    Next
    sh2.Cells(1, 1).Resize(UBound(a), UBound(a, 2)) = a
End Sub
 
Ну и все у меня записалось. Только в этом месте неувязочка:
a(i, 8) = sd.Item(a(i, 1))
т.к. на листе только 7 столбцов и в массиве, следовательно, тоже.
Поэтому сделал так:
Код
Sub NewVPR()
 
    Dim a()
    Dim i&
    Dim sd As Object
    Dim sh1 As Worksheet: Set sh1 = Workbooks("Пример.xlsb").Sheets(1)
    Dim sh2 As Worksheet: Set sh2 = ActiveSheet
    Dim Key, Item
 
    a = sh1.UsedRange.Value
    Set sd = CreateObject("Scripting.Dictionary")
    For i = 1 To UBound(a)
      Key = a(i, 1) ' получаем ключ из первого столбца текущей строки массива
      Item = a(i, 7) ' получаем элемент из седьмого столбца текущей строки массива
      sd.Add Key, Item ' и добавляем новый элемент в коллекцию
    Next
    a = sh2.UsedRange.Value
    For i = 1 To UBound(a)
        If sd.Exists(a(i, 1)) Then a(i, 1) = sd.Item(a(i, 1))
 
    Next
    sh2.Cells(1, 8).Resize(UBound(a), UBound(a, 2)) = a
End Sub
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Значит вся проблема была в отображении у меня Item? у меня просто не отображается больше 256 элементов?
 
Цитата
Hellmaster написал:
у меня просто не отображается больше 256 элементов?
Да, у Вас не отображается больше 256 элементов.
Возможно, это утешит, но ни у кого не отображается больше 256 элементов :)
 
Цитата
Hellmaster написал:
Значит вся проблема была в отображении у меня Item?
8-0
Цитата
Дмитрий(The_Prist) Щербаков написал:
окно Debug не может отображать более 256 элементов, но это не значит, что их столько
Изменено: Дмитрий(The_Prist) Щербаков - 22.01.2020 15:10:17
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Дмитрий(The_Prist) Щербаков, я поэтому и переспросил) Спасибо за помощь. Буду дальше совершенствовать свой навык!
Страницы: 1
Наверх