Подскажите, как при помощи макроса, по двум параметрам (в примере "номер продукта" и "номер магазина") из первой таблицы, записать значение третьего параметра (в примере "цена") во вторую таблицу. Функция ВПР не подходит, т.к. работает слишком долго (обычно приходится работать с файлами, где содержится более 50 тыс строк информации). Возможно сделать, что-то наподобие запроса в MS Access?
Если такая тема уже существует, скиньте плз ссылку.
Спасибо.
Добрый день
Подскажите, как при помощи макроса, по двум параметрам (в примере "номер продукта" и "номер магазина") из первой таблицы, записать значение третьего параметра (в примере "цена") во вторую таблицу. Функция ВПР не подходит, т.к. работает слишком долго (обычно приходится работать с файлами, где содержится более 50 тыс строк информации). Возможно сделать, что-то наподобие запроса в MS Access?
Если такая тема уже существует, скиньте плз ссылку.
Попробовал применить макрос на практике. Из табл. с 70 ты строками подтягиваются значения в таблицу с 70 тыс строками. В итоге excel безнадежно завис. Насколько я понял, дело в таблице откуда выбираются данные. Подскажите, возможно как-нибудь оптимизировать работу макроса, чтобы он работал с таблицами где > 200 тыс строк?
Файл во вложении содержит таблицы с 2-мя тыс строк.
Заранее спасибо.
Попробовал применить макрос на практике. Из табл. с 70 ты строками подтягиваются значения в таблицу с 70 тыс строками. В итоге excel безнадежно завис. Насколько я понял, дело в таблице откуда выбираются данные. Подскажите, возможно как-нибудь оптимизировать работу макроса, чтобы он работал с таблицами где > 200 тыс строк?
Файл во вложении содержит таблицы с 2-мя тыс строк.
Если вот этот цикл написать так - скорость увеличивается в 2 раза! [vba]
Код
For ii = 1 To a If b(ii, 1) = d(i, 1) Then If b(ii, 2) = d(i, 2) Then e(i, 1) = b(ii, 4): Exit For End If Next
[/vba]
А так будет значительно быстрее (на словаре): [vba]
Код
Sub Категория_товара3() Dim tm!: tm = Timer Dim t$
With Worksheets("Группа погрузки") a = .Cells(.Rows.Count, 2).End(xlUp).Row b = .Range("B1", "E" & a) End With With Worksheets("Исходные данные") c = .Cells(.Rows.Count, 3).End(xlUp).Row d = .Range("C1", "D" & c) e = .Range("M1", "M" & c) End With
With CreateObject("scripting.dictionary"): .comparemode = 1 For i = 1 To a .Item(b(i, 1) & "|" & b(i, 2)) = b(i, 4) Next For i = 1 To c t = d(i, 1) & "|" & d(i, 2) If .exists(t) Then e(i, 1) = .Item(t) Next End With
Если вот этот цикл написать так - скорость увеличивается в 2 раза! [vba]
Код
For ii = 1 To a If b(ii, 1) = d(i, 1) Then If b(ii, 2) = d(i, 2) Then e(i, 1) = b(ii, 4): Exit For End If Next
[/vba]
А так будет значительно быстрее (на словаре): [vba]
Код
Sub Категория_товара3() Dim tm!: tm = Timer Dim t$
With Worksheets("Группа погрузки") a = .Cells(.Rows.Count, 2).End(xlUp).Row b = .Range("B1", "E" & a) End With With Worksheets("Исходные данные") c = .Cells(.Rows.Count, 3).End(xlUp).Row d = .Range("C1", "D" & c) e = .Range("M1", "M" & c) End With
With CreateObject("scripting.dictionary"): .comparemode = 1 For i = 1 To a .Item(b(i, 1) & "|" & b(i, 2)) = b(i, 4) Next For i = 1 To c t = d(i, 1) & "|" & d(i, 2) If .exists(t) Then e(i, 1) = .Item(t) Next End With
Я ещё на словарях попробовала. В 10 раз быстрее получилось, не ожидала что словари быстрее циклов в массивах, буду теперь ими чаще пользоваться) Проверьте только правильно ли оно там вставляет, а то голову сломала об эти a b c d e
[vba]
Код
Sub Категория_товара2() t = Timer Application.ScreenUpdating = False Set dic = CreateObject("Scripting.Dictionary")
a = Worksheets("Группа погрузки").Cells(Rows.Count, 2).End(xlUp).Row b = Worksheets("Группа погрузки").Range("B1", "E" & a)
c = Worksheets("Исходные данные").Cells(Rows.Count, 3).End(xlUp).Row d = Worksheets("Исходные данные").Range("C1", "D" & c) e = Worksheets("Исходные данные").Range("M1", "M" & c)
For ii = 1 To a dic.Item(b(ii, 1) & "|" & b(ii, 2)) = b(ii, 4) Next For i = 1 To c If dic.exists(d(i, 1) & "|" & d(i, 2)) Then e(i, 1) = dic.Item(d(i, 1) & "|" & d(i, 2)) Else e(i, 1) = Empty Next Range("M1", "M" & c) = e
Application.ScreenUpdating = True Debug.Print Timer - t End Sub
[/vba] UPD код подредактировала хм.. так шапка затирается, лучше ставьте диапазон сразу тот что надо, а не с первой строки т.е. Range("C4", "D" & c) и тд
Я ещё на словарях попробовала. В 10 раз быстрее получилось, не ожидала что словари быстрее циклов в массивах, буду теперь ими чаще пользоваться) Проверьте только правильно ли оно там вставляет, а то голову сломала об эти a b c d e
[vba]
Код
Sub Категория_товара2() t = Timer Application.ScreenUpdating = False Set dic = CreateObject("Scripting.Dictionary")
a = Worksheets("Группа погрузки").Cells(Rows.Count, 2).End(xlUp).Row b = Worksheets("Группа погрузки").Range("B1", "E" & a)
c = Worksheets("Исходные данные").Cells(Rows.Count, 3).End(xlUp).Row d = Worksheets("Исходные данные").Range("C1", "D" & c) e = Worksheets("Исходные данные").Range("M1", "M" & c)
For ii = 1 To a dic.Item(b(ii, 1) & "|" & b(ii, 2)) = b(ii, 4) Next For i = 1 To c If dic.exists(d(i, 1) & "|" & d(i, 2)) Then e(i, 1) = dic.Item(d(i, 1) & "|" & d(i, 2)) Else e(i, 1) = Empty Next Range("M1", "M" & c) = e
Application.ScreenUpdating = True Debug.Print Timer - t End Sub
[/vba] UPD код подредактировала хм.. так шапка затирается, лучше ставьте диапазон сразу тот что надо, а не с первой строки т.е. Range("C4", "D" & c) и тдLeanna
Лучше день потерять, потом за пять минут долететь!
Сообщение отредактировал Leanna - Понедельник, 30.03.2015, 23:11
Да, создать массив с нуля будет побыстрее чем взять с листа - но иногда нужно дополнить то, что уже есть на листе. Но в общем это копейки... e = Range("M1", "M" & 70000) отрабатывает за 0 или 0,0078125
Да, создать массив с нуля будет побыстрее чем взять с листа - но иногда нужно дополнить то, что уже есть на листе. Но в общем это копейки... e = Range("M1", "M" & 70000) отрабатывает за 0 или 0,0078125Hugo
Sub Категория_товара() Dim a&, b(), c&, d(), e(), f As Range, i&
With Worksheets("Группа погрузки") a = .Cells(.Rows.Count, 2).End(xlUp).Row b = .Range("B2:E" & a).Value End With With Worksheets("Исходные данные") c = .Cells(.Rows.Count, 3).End(xlUp).Row d = .Range("C4:D" & c).Value Set f = .Range("M4:M" & c) e = f End With
With CreateObject("Scripting.Dictionary") For i = 1 To a - 1 .Item(b(i, 1) & "|" & b(i, 2)) = b(i, 4) Next For i = 1 To c - 3 e(i, 1) = .Item(d(i, 1) & "|" & d(i, 2)) Next End With f = e End Sub
[/vba]
а так еще чуть проще и чуть быстрее: [vba]
Код
Sub Категория_товара() Dim a&, b(), c&, d(), e(), f As Range, i&
With Worksheets("Группа погрузки") a = .Cells(.Rows.Count, 2).End(xlUp).Row b = .Range("B2:E" & a).Value End With With Worksheets("Исходные данные") c = .Cells(.Rows.Count, 3).End(xlUp).Row d = .Range("C4:D" & c).Value Set f = .Range("M4:M" & c) e = f End With
With CreateObject("Scripting.Dictionary") For i = 1 To a - 1 .Item(b(i, 1) & "|" & b(i, 2)) = b(i, 4) Next For i = 1 To c - 3 e(i, 1) = .Item(d(i, 1) & "|" & d(i, 2)) Next End With f = e End Sub
[offtop]как зачем? для удобочитаемости... чтобы описание диапазона в коде было только в одном месте и, желательно, рядом с описанием других исходных данных (в данном случае - других диапазонов и имен листов книги), тогда, при необходимости переназначения диапазона, не придется "лазить" в поисках по всему коду (особенно, если код большой), и уже точно "не забудешь" переназначить ЭТОТ ЖЕ диапазон где-нибудь в другом месте кода, т.к. других мест просто не будет... я думаю, что "лишняя" переменная, занимающая 4 байта памяти, это небольшая плата за такое удобство. а если у этой переменной еще и осмысленное имя, то еще и код будет понятнее (более удобочитаемый)...
[offtop]как зачем? для удобочитаемости... чтобы описание диапазона в коде было только в одном месте и, желательно, рядом с описанием других исходных данных (в данном случае - других диапазонов и имен листов книги), тогда, при необходимости переназначения диапазона, не придется "лазить" в поисках по всему коду (особенно, если код большой), и уже точно "не забудешь" переназначить ЭТОТ ЖЕ диапазон где-нибудь в другом месте кода, т.к. других мест просто не будет... я думаю, что "лишняя" переменная, занимающая 4 байта памяти, это небольшая плата за такое удобство. а если у этой переменной еще и осмысленное имя, то еще и код будет понятнее (более удобочитаемый)...KSV