Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Microsoft Office и VBA программирование > Microsoft Office Excel
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 12.04.2009, 10:41   #1
skif93
Пользователь
 
Регистрация: 24.11.2007
Сообщений: 12
По умолчанию Падение быстродействия в макросе

При открытии книги время выполнения макроса: 1797
Все последующие запуски:
4890
4859
4860
4953
Объясните пожалуйста где грабли?
Прошу сильно не пинать - с VBA на Вы.
Заранее спасибо.
Вложения
Тип файла: txt Макрос.txt (5.1 Кб, 132 просмотров)
skif93 вне форума Ответить с цитированием
Старый 12.04.2009, 11:11   #2
EducatedFool
Программист VBA
СуперМодератор
 
Аватар для EducatedFool
 
Регистрация: 13.07.2008
Сообщений: 6,856
По умолчанию

Проблема в строках
Код:
Do While Worksheets("Салоны").Cells(nRow, 1).Value <> ""
Do While Worksheets("СтрахованиеТранспорта").Cells(nRow, 10).Value<> ""
        Do While (Worksheets("Салоны").Cells(nRow, 3).Value = sVid And ...
Из-за перебора большого количества ячеек макрос начинает тормозить.
Возможно, какое-то условие не срабатывает, и макрос перебирает намного больше строк, чем надо.

Ну и вот это из макроса надо убрать:
Код:
Range("A" & nRow & ":E" & nRow).Select
                ramka
                Range("A" & nRow).Select
То же самое можно реализовать без выделения ячеек.

В общем, много всяких нюансов.
Но главное - надо сначала считывать весь диапазон ячеек с листа в массив, и потом уже обрабатывать значения массива.

Как раз вчера переделывал аналогичный макрос (который я писал год назад, когда только начинал знакомство с VBA). Применив вышеописанный способ, увеличил быстродействие кода в 9 раз.

Прикрепите к сообщению файл с данными, иначе оптимизировать макрос будет крайне сложно.

PS: Вместо API-функции GetTickCount можно использовать встроенную в VBA функцию Timer (например, t=Timer)
EducatedFool вне форума Ответить с цитированием
Старый 12.04.2009, 11:15   #3
skif93
Пользователь
 
Регистрация: 24.11.2007
Сообщений: 12
По умолчанию

Но при первом запуске все ок
skif93 вне форума Ответить с цитированием
Старый 12.04.2009, 11:18   #4
EducatedFool
Программист VBA
СуперМодератор
 
Аватар для EducatedFool
 
Регистрация: 13.07.2008
Сообщений: 6,856
По умолчанию

Цитата:
Сообщение от skif93 Посмотреть сообщение
Но при первом запуске все ок
Вы называете удовлетворительным время работы макроса в 2 секунды?
В таком случае можно подождать и 5 сек...

Вообще, этот макрос после оптимизации может выполнить всю работу максимум за 0,5 сек.

А какой лист активен при открытии книги, и какой лист - при последующих запусках макроса?

Последний раз редактировалось EducatedFool; 12.04.2009 в 11:21.
EducatedFool вне форума Ответить с цитированием
Старый 12.04.2009, 11:38   #5
skif93
Пользователь
 
Регистрация: 24.11.2007
Сообщений: 12
По умолчанию

Лист активен всегда один "Отчеты"
А 2 секунды это дома, а на работе 20-25
И ещё интересная штука - убрал из таблицы все данные не относящиеся к работе макроса и время работы макроса сократилось, но все равно первый запуск работает быстрее последующих.
Вложения
Тип файла: rar Пример.rar (945.4 Кб, 14 просмотров)
skif93 вне форума Ответить с цитированием
Старый 12.04.2009, 11:50   #6
EducatedFool
Программист VBA
СуперМодератор
 
Аватар для EducatedFool
 
Регистрация: 13.07.2008
Сообщений: 6,856
По умолчанию

Сейчас попробую оптимизировать.
На сначала придётся разобраться, что этот макрос делает (на что уйдёт много времени)
Если есть Skype - звоните (это позволит сэкономить кучу времени)

Проблема очевидна - вы поочерёдно перебирали 33 тысячи ячеек.
Конечно, макрос будет тормозить.

Есть 3 варианта решения:
1) Задействовать встроенный автофильтр для фильтрации по дате, и обрабатывать только отфильтрованные строки
2) Считать весь диапазон ячеек в массив, и обрабатывать данные из массива.
3) Для формирования отчёта использовать сводные таблицы (в них я не разбираюсь, но по идее это позволило бы обойтись совсем без макросов)

Последний раз редактировалось EducatedFool; 12.04.2009 в 12:09.
EducatedFool вне форума Ответить с цитированием
Старый 12.04.2009, 12:25   #7
skif93
Пользователь
 
Регистрация: 24.11.2007
Сообщений: 12
По умолчанию

Где бы литературу посмотреть по работе с массивами
А строк будет порядка 55тыс
skif93 вне форума Ответить с цитированием
Старый 12.04.2009, 14:31   #8
EducatedFool
Программист VBA
СуперМодератор
 
Аватар для EducatedFool
 
Регистрация: 13.07.2008
Сообщений: 6,856
По умолчанию

Вариант с автофильтром показал очень низкую производительность...
(см. вложение)

Проще всего обойтись совсем без макросов.
Сформируйте на новом листе сводную таблицу, которая будет брать данные непосредственно с листа СтрахованиеТранспорта
А начальную и конечную дату будете выбирать в самой сводной таблице.

Вариант с автофильтром показал очень низкую производительность...
(см. вложение)

Во вложении макрос формирует список на листе Салоны (этот лист скрыт), но не обрабатывает его - на основании этого списка уже построена сводная таблица, которую достаточно просто обновить:

Код:
Sub ПоСалону()
    StartTime = Timer: Application.ScreenUpdating = False
    dStart = CDbl(OT.[c1]): dEnd = CDbl(OT.[c2])
    OT.Visible = True: SA.UsedRange.Offset(1).Clear: SA.Visible = xlSheetHidden
    ST.Range("a7").AutoFilter Field:=6, Criteria1:=">" & dStart - 1, Operator:=xlAnd, Criteria2:="<" & dEnd + 1
    Dim ra As Range, ro As Range, SALONrow As Range
    Set ra = Intersect(ST.Cells.SpecialCells(xlCellTypeVisible), ST.UsedRange, ST.UsedRange.Offset(6))

    For Each ro In ra.EntireRow
        With ro
            If Trim(.Cells(12)) <> "" Then
                Set SALONrow = SA.Range("A" & SA.Rows.Count).End(xlUp).Offset(1).Resize(, 5)
                SALONrow.Value = Array(.Cells(4), .Cells(5), UCase(.Cells(16)), 1, .Cells(12))
            End If
        End With
    Next ro
    ST.ShowAllData     ' сброс автофильтра
    
    SA.UsedRange.Sort SA.[a2], xlAscending, SA.[b2], , xlAscending, SA.[c2], xlAscending, xlYes ' сортировка
    SV.PivotTables(1).PivotCache.Refresh ' обновление сводной таблицы
    
    Set ra = OT.Range("e65000").End(xlUp).Offset(1)
    ra = Now: ra.Next = FormatNumber(Timer - StartTime, 2) & " сек."
End Sub
Вариант с массивами в этом случае будет работать быстрее - считывание данных с листа в массив длится около 0,5 сек., плюс на всё остальное уйдёт примерно столько же времени.
Попробуйте этот вариант на досуге:
Код:
        Set ra = Intersect(ST.UsedRange, ST.UsedRange.Offset(6), ST.Columns("a:q"))
        'Debug.Print ra.Address
        arr = ra.Value ' считываем диапазон ячеек в массив
        ' arr - двумерный массив. обращаться к нему можно так: x=arr(5,3)
Вложения
Тип файла: rar Пример оптимизированный.rar (982.3 Кб, 89 просмотров)
EducatedFool вне форума Ответить с цитированием
Старый 12.04.2009, 14:49   #9
skif93
Пользователь
 
Регистрация: 24.11.2007
Сообщений: 12
По умолчанию

Спасибо большое за оперативность и примеры.
Завтра буду пробовать на "живых данных".
skif93 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Speed drop падение скорости Ivn Софт 1 12.02.2009 07:17
Функции в макросе. Rom1k06 Microsoft Office Excel 7 19.10.2008 11:22
Ошибка и падение (выход) Excel при выполнении сложной программы на VBA Serge_Bliznykov Microsoft Office Excel 6 13.08.2008 16:50
Суммирование элементов массива в макросе IgorKr Общие вопросы C/C++ 1 14.04.2008 01:22
Константы в Макросе valerij Microsoft Office Excel 2 03.02.2008 23:33