Добрый день, планета Excel! Помогите в решении задачи (пример во вложении): Необходимо подтянуть данные в столбец "Марка провода" из листа "Компоненты" с условием : 1. Если в пролете используется 1 провод, то =Название компонента, т.е. одножильный провод. 2. Если провода больше одного, то =сцепить("провод"; кол-во проводов;"х"; название компонента1). Сложность формулы в том, что при большом кол-ве данных (кол-во пролетов бывает до 200тыс., а компонентов до 900тыс.) расчет длится очень долго. Помогите ускорить расчет формулы! Быть может возможно вычисление макросом вместо формулы? Помогите написать макрос. Формула в виде: =ЕСЛИ(СЧЁТЕСЛИ(Компоненты!$A$2:$A$443;A3)=1;ИНДЕКС(Компоненты!$C$2:$C$443;ПОИСКПОЗ(A3;Компоненты!$A$2:$A$443;0));СЦЕПИТЬ("Провод ";СЧЁТЕСЛИ(Компоненты!$A$2:$A$443;A3);"х";ИНДЕКС(Компоненты!$D$2:$D$443;ПОИСКПОЗ(A3;Компоненты!$A$2:$A$443;0))))
Лучше всего для ускорения - изменить исходные данные. Например, напрашивается (если невозможно изменить количество столбцов) в столбце "Название компонента 1" на листе "Компоненты" вывести количество используемых проводов, затем заменить его на значение и убрать дубликаты. Тогда можно в каждой ячейке не считать 2 раза количество пролетов, а посчитать 1 раз и в другом месте. Если можно создать дополнительный столбец(столбцы), то см. файл. Еще упростит, если при этом вставить значения и убрать дубликаты. Макросом тоже можно :)
Макросом на словаре не сложно - сперва пробежать по компонентам и собрать словарь (или можно два, что проще и думаю будет быстрее работать) с названием и количеством. Затем циклом по пролётам берём из словаря что собрали. Быстро на любом количестве.
Всего делов:
Код
Sub tt()
Dim a(), i&, cDic As Object, nDic As Object
Set cDic = CreateObject("Scripting.Dictionary"): cDic.comparemode = 1
Set nDic = CreateObject("Scripting.Dictionary"): nDic.comparemode = 1
a = Sheets("Компоненты").[a1].CurrentRegion.Value
For i = 2 To UBound(a)
If Not cDic.exists(a(i, 1)) Then cDic.Item(a(i, 1)) = a(i, 4)
nDic.Item(a(i, 1)) = nDic.Item(a(i, 1)) + 1
Next
a = Sheets("Пролеты").[a1].CurrentRegion.Value
For i = 3 To UBound(a) 'по месту!!!
If nDic.Item(a(i, 1)) = 1 Then
a(i, 3) = "Провод " & cDic.Item(a(i, 1))
Else
a(i, 3) = "Провод " & nDic.Item(a(i, 1)) & "х" & cDic.Item(a(i, 1))
End If
Next
Sheets("Пролеты").[a1].CurrentRegion.Value = a
End Sub
Sub tt()
Dim a(), b(), i&, cDic As Object, nDic As Object
Set cDic = CreateObject("Scripting.Dictionary"): cDic.comparemode = 1
Set nDic = CreateObject("Scripting.Dictionary"): nDic.comparemode = 1
a = Sheets("Материалы").[a1].CurrentRegion.Value
For i = 2 To UBound(a)
If Not cDic.exists(a(i, 1)) Then cDic.Item(a(i, 1)) = a(i, 4)
nDic.Item(a(i, 1)) = nDic.Item(a(i, 1)) + 1
Next
a = Sheets(1).[a1].CurrentRegion.Columns(2).Value
b = Sheets(1).[a1].CurrentRegion.Columns(9).Value
For i = 2 To UBound(a) 'по месту!!!
If nDic.exists(a(i, 1)) Then
If nDic.Item(a(i, 1)) = 1 Then
b(i, 1) = "Провод " & cDic.Item(a(i, 1))
Else
b(i, 1) = "Провод " & nDic.Item(a(i, 1)) & "х" & cDic.Item(a(i, 1))
End If
End If
Next
Sheets(1).[a1].CurrentRegion.Columns(9).Value = b
End Sub
Hugo, работает! В поле марка провода, содержащей 280тыс. строк, потдягивает данные из листа с количеством строк 930 тыс. за примерно 30-40 сек !!! Знание - это сила! Благодарю!!!
Сразу нужно было говорить про такое количество - я бы ещё информацию в статусбар приладил, чтоб видеть что процесс идёт (40 секунд - всёж есть время закрасться сомнению - а не висит ли? ) Позже чуть подправлю, как время будет..
Попробуйте так - ещё чуть сэкономил память:
Скрытый текст
Код
Sub tt()
Dim a(), b(), i&, cDic As Object, nDic As Object
Set cDic = CreateObject("Scripting.Dictionary"): cDic.comparemode = 1
Set nDic = CreateObject("Scripting.Dictionary"): nDic.comparemode = 1
a = Sheets(2).[a1].CurrentRegion.Columns(1).Value
b = Sheets(2).[a1].CurrentRegion.Columns(4).Value
For i = 2 To UBound(a)
If i Mod 1000 = 0 Then Application.StatusBar = "Анализ материалов, строка " & i
If Not cDic.exists(a(i, 1)) Then cDic.Item(a(i, 1)) = b(i, 1)
nDic.Item(a(i, 1)) = nDic.Item(a(i, 1)) + 1
Next
a = Sheets(1).[a1].CurrentRegion.Columns(2).Value
b = Sheets(1).[a1].CurrentRegion.Columns(9).Value
For i = 2 To UBound(a) 'по месту!!!
If i Mod 1000 = 0 Then Application.StatusBar = "Генерация результата, строка " & i
If nDic.exists(a(i, 1)) Then
If nDic.Item(a(i, 1)) = 1 Then
b(i, 1) = "Провод " & cDic.Item(a(i, 1))
Else
b(i, 1) = "Провод " & nDic.Item(a(i, 1)) & "х" & cDic.Item(a(i, 1))
End If
End If
Next
Application.StatusBar = "Выгрузка результата"
Sheets(1).[a1].CurrentRegion.Columns(9).Value = b
Application.StatusBar = False
End Sub
Выше добавил код. Статусбар - это полоса в самом низу таблицы, там где изначально написано "Ready" (ну или что там в русском, не помню - вероятно "Готово"?)
Вообще статусбар тоже потребляет процессорное время - поэтому я только каждую тысячную строку обрабатываю, чтоб только чуть затормозить процесс. Почему зависает - не знаю, по идее не должно. Но вообще задача объёмная, всё может быть... Если первый вариант работает стабильнее - используйте первый вариант. Хотя странно, во втором по идее меньше памяти используется, а статусбар не такое уж обременение...
Т.е. если в пролете компонент встречается только 1 раз, то само "Название компонента", если нет, то сцепить (назв. компонента 1; кол-во компонента; "х"; назв. комп 2) . Так как встречается не только провод, но и кабель.
Вот почему сразу не показать реальную задачу? Сейчас код в третий раз изменю - но порядок в коде наводить уже не буду, сами виноваты...
Скрытый текст
Код
Option Explicit
Sub tt()
Dim a(), b(), i&, cDic As Object, cDic2 As Object, nDic As Object
Set cDic = CreateObject("Scripting.Dictionary"): cDic.comparemode = 1
Set cDic2 = CreateObject("Scripting.Dictionary"): cDic2.comparemode = 1
Set nDic = CreateObject("Scripting.Dictionary"): nDic.comparemode = 1
a = Sheets(2).[a1].CurrentRegion.Columns(1).Value
b = Sheets(2).[a1].CurrentRegion.Columns(4).Resize(, 2).Value
For i = 2 To UBound(a)
If i Mod 1000 = 0 Then Application.StatusBar = "Анализ материалов, строка " & i
If Not cDic.exists(a(i, 1)) Then
cDic.Item(a(i, 1)) = b(i, 2)
cDic2.Item(a(i, 1)) = b(i, 1)
End If
nDic.Item(a(i, 1)) = nDic.Item(a(i, 1)) + 1
Next
a = Sheets(1).[a1].CurrentRegion.Columns(2).Value
b = Sheets(1).[a1].CurrentRegion.Columns(9).Value
For i = 2 To UBound(a) 'по месту!!!
If i Mod 1000 = 0 Then Application.StatusBar = "Генерация результата, строка " & i
If nDic.exists(a(i, 1)) Then
If nDic.Item(a(i, 1)) = 1 Then
b(i, 1) = cDic2.Item(a(i, 1)) & " " & cDic.Item(a(i, 1))
Else
b(i, 1) = cDic2.Item(a(i, 1)) & " " & nDic.Item(a(i, 1)) & "х" & cDic.Item(a(i, 1))
End If
End If
Next
Application.StatusBar = "Выгрузка результата"
Sheets(1).[a1].CurrentRegion.Columns(9).Value = b
Application.StatusBar = False
End Sub
и не важно, кабель там или что... Проверьте - так хотели? Потому что я не понял, как хотели
Можно что-то такое сообразить, но зачем? Тогда для значений, которых больше одного - нужно сцеплять название1 и название2, а для тех, кто 1 - писать название0. Т.е нужно в код внедрять ещё один словарь. Ну внедряйте, если Вам заняться нечем...
Вообще-то можно всё делать на одном словаре - так я в начале и планировал, но чтоб код был проще - сделал два словаря. Теперь уже три, далее четыре... Если изменить изначальный подход - сохранять в словаре номер строки массива, где встречен ключ, и там же собирать суммы (хотя для сумм всёж лучше другой словарь или массив) - то памяти затратится больше, а словарей меньше.
Hugo, добрый день! Возможно подскажите по моему условному примеру. Имеется сводная таблица по вертикали фамилии, по горизонтали даты. Нужно на другом листе (1) собрать повторяющиеся фамилии в одну и (2) сложить при совпадении значения в соответствующих датах. Второе я сделал, первое пока не соображу какую формулу применить. Массив данных по вертикали будет около 2-3 тысяч строк. Поэтому потребовалась формула. В файле 2 листа, 1 лист исходные данные (Свод), на 2 листе (Итог) то, что хочу получить.
Если нужны формулы - то используйте СУММПРОИЗВ(). Или кажется массивную СУММЕСЛИ() - но я не формулист, могу ошибаться. Да у Вас ведь уже сделано формулами... Ну а если делать макросом - массивы-словари, или SQL На большом количестве конечно макросом будет легче файл, и быстрее получите результат.
P.S. Как выбрать уникальные файмилии - на форуме были примеры формул, я правда подсказывать не берусь. Я обычно делаю макросом, ну или что мешает вручную удалить дубликаты?
Спасибо большое всем! Протянул свою формулу на весь объём. Объём оказался в 45 000 ячеек. Эксель при изменении одной ячейки пересчитывал мне все ячейки около 40 минут. Попробую применить ваши формулы. По фамилиям искал, возможно не так задавал поиск. Ещё сейчас понадобилась автоматическая сортировка первого столбца по алфавиту. Но чтобы сортировка была не одной ячейки, а построчно.