Страницы: 1
RSS
Как увеличить скорость работы макроса
 
Доброе время суток. Нужен макрос для реализации автофильтра по содержимому в двух и более столбцах. Приведенный вариант необходимую задачу решает, но крайне медленно. Есть - ли способ ускорить работу ?  
 For n = 3 To 1500  
   If IsEmpty(Cells(n, 1).Value) = True Then  
   If (Cells(n, 6).Value) <= 0 Then  
   Cells(n, 1).EntireRow.Hidden = True  
 End If    
 End If  
 Next n
 
Попробуйте так  
 
Sub Макрос1()  
Dim n As Long  
   With Application  
       .ScreenUpdating = False  
       .Calculation = xlCalculationManual  
       For n = 3 To 1500  
           If IsEmpty(Cells(n, 1)) Then  
               If (Cells(n, 6)) <= 0 Then  
                   Cells(n, 1).EntireRow.Hidden = True  
               End If  
           End If  
       Next n  
       .Calculation = xlCalculationAutomatic  
       .ScreenUpdating = True  
   End With  
   MsgBox "Строки скрыты!", 64, ""  
End Sub
 
---  
Вот так - практически мгновенно:  
 
Sub Силычу()  
Dim rng, out, i, Пусто  
Dim Колонка1, Колонка2  
Set rng = Range(Cells(3, 1), Cells(1500, 1))  
Колонка1 = rng  
Колонка2 = rng.Offset(0, 5)  
Пусто = True  
' Накопить  
With rng.Cells(1, 1)  
For Each v In Колонка1  
i = i + 1  
If v = "" Then  
If Колонка2(i, 1) <= 0 Then  
If Пусто Then  
Set out = .Offset(i - 1)  
Пусто = False  
Else  
Set out = Union(out, .Offset(i - 1))  
End If  
End If  
End If  
Next  
End With  
' Скрыть  
With Application  
.Calculation = xlCalculationManual  
.ScreenUpdating = False  
out.EntireRow.Hidden = True  
.Calculation = xlCalculationAutomatic  
.EnableEvents = True  
End With  
End Sub  
 
Комментарии:  
1. Количество медленных обращений VBA -> Excel сокращено за счет того, что диапазоны с условиями скопированы в массивы VBA и там уже анализируются.    
2. Медленное скрытие строк сделано за один раз: сначала с помощью Union() определили скрывемый диапазон, а затем одним действием скрыли все строки.  
3. Использование With rng.Cells(1, 1) позволило быстрее обращаться к ячейкам скрывемого диапазона с помощью .Offset(i - 1)  
 
---  
ZVI
 
---  
P.S. Заметил свою опечатку:  
 
Вместо  
.EnableEvents = True  
 
Должно быть:  
.ScreenUpdating = True  
 
---  
ZVI
 
Я бы еще вместо If v = "" Then  
If Колонка2(i, 1) <= 0 Then  
записал бы If v = "" And Колонка2(i, 1) <= 0 Then  
Но это на любителя, хотя пару операторов в цикле убирает.  
 
И еще. Мне проверять лень, что работает быстрее, но почему бы не навесить реальный автофильтр?  
   Range("A3:E1500").Select  
   Selection.AutoFilter  
   Selection.AutoFilter Field:=1, Criteria1:="<>"  
   Selection.AutoFilter Field:=6, Criteria1:="<=0", Operator:=xlAnd
 
"почему бы не навесить реальный автофильтр?"  
 
во-во, сразу хотел сказать..  
хорошо, что дочитал до конца :)  
 
не надо делать циклов там, где за вас это может сделать иксель!
 
---  
Комментарий по замене фрагмента кода:  
 
If a = "" Then  
If b <= 0  
'...  
End If  
End If  
 
на:  
 
If a = "" And b <= 0 Then ...  
'...  
End If  
 
Второй вариант конечно же синтаксически лучше, но работает он примерно в 1.5 раза дольше, чем первый.  
 
Совет применить автофильтр совершено правильный.  
Это если к вопросу подходить творчески.  
Но, с другой стороны, мы не знаем, что там за данные.  
И если, например, 1 и 2 строки пустые, а с 3-й строки начинаются данные, то автофильтр станет в 3-ю строку данных и ее саму уже не отфильтрует.  
 
Полагаю, что у Силыча теперь достаточно информации для размышления.  
---  
ZVI
 
{quote}{login=ZVI}{date=28.04.2008 09:42}{thema=Re: Как увеличить скорость работы макроса}{post}---  
И если, например, 1 и 2 строки пустые, а с 3-й строки начинаются данные, то автофильтр станет в 3-ю строку данных и ее саму уже не отфильтрует.  
 
ZVI{/post}{/quote}  
 
для этого можно в диапазон одну пустую строчку специально добавить, чтоб уж совсем..
 
т.е. не использовать запись макрорекордера с ее selection.autofilter, а указать диапазон по-мужски прямо, и добавить в начало пустую строку(или непустую)
 
{quote}{login=Лузер™}{date=28.04.2008 08:53}{thema=}{post}Я бы еще вместо If v = "" Then  
If Колонка2(i, 1) <= 0 Then  
записал бы If v = "" And Колонка2(i, 1) <= 0 Then  
Но это на любителя, хотя пару операторов в цикле убирает.  
 
И еще. Мне проверять лень, что работает быстрее, но почему бы не навесить реальный автофильтр?  
   Range("A3:E1500").Select  
   Selection.AutoFilter  
   Selection.AutoFilter Field:=1, Criteria1:="<>"  
   Selection.AutoFilter Field:=6, Criteria1:="<=0", Operator:=xlAnd{/post}{/quote}  
А не скроет ли автофильтр все и пустые и <=0.  
Т.е вариант пусто больше 0 тоже будет скрыть?!    
Автофильтр работает по моему немного быстрее, хотя надо проверять на машине пользователя.
 
насколько я понимаю, сначала показываются все непустые строки, а потом среди этих непустых выбираются меньшие либо равные нулю..
 
Да, но по условиям первый столбец пусто второй меньше нуля. А если пусто и больше должен отображаться. Автофильтр, помоему, скроет сначала все пусто, затем меньше 0? Или я ошибаюсь?
 
Конечно, условие для автофильтра я записал с ошибкой.  
Автофильтр оставляет видимыми строки соотбетствующие условию. Стало быть надо:  
Selection.AutoFilter Field:=1, Criteria1:="<>"  
Selection.AutoFilter Field:=6, Criteria1:=">0", Operator:=xlAnd  
Оставить непустые и больше 0 -> пустые и меньше нуля будут скрыты.  
И... Игорь прав.  
Когда скрываем пустые, то скроются и больше, и меньше нуля, т.е. скроется больше строк чем нужно.  
Стало быть только циклом в вба.
 
Либо навешивать доп столбец и по нему фильтровать  
Столбец типа: =ЕПУСТО(А1)*(Е1<=0)  
Все зависит насколько быстрее будет доп.строка+доп.столбец+автофильтр vs цикл вба
 
а как насчет расширенного автофильтра?  
http://www.planetaexcel.ru/tip.php?aid=48
 
С использованием расширенного автофильтра. См. пример, при необходимости количество критериев можно расширить аналогически
 
Огромное Спасибо всем откликнувшимся. Воспользовался вариантом предложенным ZVI. Вариант с использованием автофильтра не проходит. так как не все пустые строки первого столбца нужно скрывать, а только те, у которых третий столбец не пустой, а в шестом зачение 0 или меньше.До макроса использовал вариант с формулами ЕСЛИ И в отдельном столбце и автофильтром на значение (0 или 1), но макрос имхо удобней.
 
Поднимаю трехлетнюю тему :)  
Имею массив данных с данными, где есть и столбец с датами. Макрос работает с данными за определенный период (один какой-то месяц). Сейчас он перебирает весь массив и по оператору ИФ (для даты) выбирает нужные строки для работы. :) Дооолго работает..  
Но, тут я попал на эту тему... и задумался... может получится ускорить работу макроса..  
 
Знатоки, подскажите..  
Если я применю автофильтр в макросе, то оператор цикла ФОР будет перебирать только видимые записи (строки)?    
Если да, то как этому оператору задать первую и конечную видимую строку?
 
> Если я применю автофильтр в макросе, то оператор цикла ФОР будет перебирать только видимые записи (строки)?    
 
Нет. Проверяйте свойство rows(i).hidden или используйте цикл по видимым ячейкам, например  
Dim c As Range  
For Each c In [A2:A10].SpecialCells(xlCellTypeVisible)
Однако, метод SpecialCells имеет ограничение в версиях XL до 2007 включительно: он возвращает диапазон не более 8192 прямоугольных областей. То есть если столбец таблицы имеет более 16384 ячеек, надо обрабатывать его кусками по 16384 ячейки.  
 
Или загружайте всю таблицу в массив и обрабатывайте средствами VBA.
 
вы бы выложили листинг?
 
{quote}{login=Казанский}  
Или загружайте всю таблицу в массив и обрабатывайте средствами VBA.{/post}{/quote}  
 
Спасибо, понял... У меня макрос работает с 5 большими таблицами, и если загружать эти таблицы в массивы, и работать средствами VBA - придется много править (уж много условий с ссылками на таблицы).  
Скажите, а если сделать так:  
Я применю автофильтр, выделю видимые данные, и вставлю их в отдельный лист (созданный специально для вычислений). Так у меня получится таблица только с нужными мне строками (Или же я смогу скопировать не более 8192 прямоугольных областей (у меня ексель 2003)?  
В таком случае в макросе мне надо поменять только вот это Set ShU = Sheets("Услуги") на, скажем Set ShU = Sheets("Услуги_для_макроса")
 
можно использовать расширенный фильтр для копирования нужных данных на отдельный лист..  
 
но макросом перебратьв массиве будет побыстрее
Живи и дай жить..
 
i.s.o  
> Или же я смогу скопировать не более 8192 прямоугольных областей (у меня ексель 2003)?  
Не знаю как в 2003, а в 2007 Вы НЕ сможете скопировать диапазон с наложенным фильтром, если в нем более 8192 отфильтрованных областей - ошибка "Невозможно создать или использовать ссылку на диапазон данных...".  
 
слэн  
В 2007 и ранее расширенный фильтр может копировать отобранные данные только на этот лист. Интересно - в 2010 сняли это ограничение?
 
Ну, по теории, у меня не будет много областей, посколько данные вносятся ежедневно, и при фильтре попадут в одну область. И если же будут внесены изменения в таблицу, то не думаю, что аж 8 тыщ... :)  
 
Споткнулся здесь:  
 
Dim d1 As Date, d2 As Date  
 
d1 = ShR.Cells(3, 1)' Начальная дата - беру с другого листа, он определен ShR  
d2 = ShR.Cells(5, 1)' Конечная дата - беру с другого листа, он определен ShR  
 
   Rows("5:5").Select  
   Selection.AutoFilter  
   Range("B5").Activate  
   Selection.AutoFilter Field:=5, Criteria1:=">=" & d1, Operator:=xlAnd _  
       , Criteria2:="<=" & d2  
 
Этот код ставит автофильтр, и пременяет его. НО все отфильтрованные строки становятся невидимыми. Почему?    
А в ручную все ок.  
И ещё, а как Деактивировать автофильтр?
 
Вот попробовал сделать образец...  
И ещё я не знаю, как в макросе выделить отфильтрованые записи...  
<EM><STRONG>Файл удален</STRONG> - велик размер - [<STRONG>МОДЕРАТОРЫ</STRONG>]</EM>
 
> Споткнулся здесь  
С датами в автофильтре проблема. Используйте числовые переменные. Если будут только даты (без времени суток), можно использовать Long:  
 
Dim d1 As Long, d2 As Long  
d1 = ShR.Cells(3, 1)' Начальная дата - беру с другого листа, он определен ShR  
d2 = ShR.Cells(5, 1)' Конечная дата - беру с другого листа, он определен ShR  
'...  
Selection.AutoFilter Field:=5, Criteria1:=">=" & d1, Operator:=xlAnd, Criteria2:="<=" & d2  
 
С временем суток - еще интереснее: надо использовать текстовое представление числа, но с десятичным разделителем точка:  
 
Dim d1 As Double, d2 As Double  
d1 = ShR.Cells(3, 1)' Начальная дата и время  
d2 = ShR.Cells(5, 1)' Конечная дата и время  
'...  
Selection.AutoFilter Field:=5, Criteria1:=">=" & Str(d1), Operator:=xlAnd, Criteria2:="<=" & Str(d2)  
 
> И ещё, а как Деактивировать автофильтр?  
Selection.AutoFilter  
или  
activesheet.showalldata
 
{quote}{login=Казанский}{date=31.03.2011 05:46}{thema=}{post}>  можно использовать Long:  
 
Dim d1 As Long, d2 As Long  
d1 = ShR.Cells(3, 1)' Начальная дата - беру с другого листа, он определен ShR  
d2 = ShR.Cells(5, 1)' Конечная дата - беру с другого листа, он определен ShR  
'...  
Selection.AutoFilter Field:=5, Criteria1:=">=" & d1, Operator:=xlAnd, Criteria2:="<=" & d2  
или  
activesheet.showalldata{/post}{/quote}  
 
Спасибо, сработало!
Страницы: 1
Читают тему
Наверх