Excel VBA, Программирующий с Массивами: Передать их или Не Передать их?

Вопрос: Я задаюсь вопросом, который является оптимальным решением для контакта с Массивами в Excel 2003 VBA

Фон: у Меня есть Макрос в Excel 2003, который является более чем 5 000 строк. Я создал его за прошлые 2 года, добавив новые опции как новые Процедуры, который помогает сегментировать код и отладку, измениться или добавить к той функции. Оборотная сторона - то, что я использую большую часть той же основной информации в нескольких процедурах, которая требует, чтобы я загрузил ее в массивы с незначительными различиями многократно. Я теперь сталкиваюсь с проблемами с продолжительностью времени выполнения, таким образом, я теперь могу сделать, полное переписывает.
Этот файл используется для захвата нескольких объектов производства потоков (до 4 различных взлетов набора максимум с в общей сложности 10 отличными потоками, до 1 000 шагов каждый) с информацией, являющейся конкретным Потоком, Подпоток, специфичный для группировки / сортировка целей и Данных (таких как перемещения, материально-технические ресурсы, CT...)
Это затем засунет данные на несколько листов, используемых для управления процессом, использующим таблицы данных, которые будут просмотрены, диаграммы и Форматирование Ячейки для обозначения возможности технологического маршрута / история.
Поток находится в Excel File, в то время как Производственные данные считаны в с 7 различными получениями по запросу OO4O Oracle SQL, некоторые снова использованные многократно

Массивы:
arrrFlow (1 - 1 000, 1 - 4) как Тип записи с 4 строками
arrrSubFlow (1 - 1 000, 1 - 10) как Тип записи с 4 строками, 2 целыми числами и 1 единственным
arrrData (1 - 1 000, 1 - 10) как Тип записи с 1 строкой, 4 целыми числами, 12 longs и 1 единственным
arriSort (1 - 1 000, 1 - 4) как Целое число (Используемый в качестве массива указателей для сортировки Потока Поток Sub и Данные в Группе, Sub Group и Шаге заказывают при отъезде исходных массивов в порядке Шага),

Возможности:
1) Перепишите макрос в одну большую процедуру, которая загружает данные в основные массивы, определенные размеры в рамках Процедуры однажды
Pro: Определенный размеры в Процедуре, а не как Общедоступная переменная в Модуле и не передал.
Довод "против": Тяжелее отлаживать с одной мега процедурой вместо нескольких меньших.

2) Сохраните макрос с несколькими процедурами, но передачу Массивов
Pro: Легче отладить код с несколькими меньшими процедурами.
Довод "против": передача массивов (дорогой?)

3) Сохраните макрос с несколькими процедурами, но с Массивами быть Общедоступными переменными Dim'ed в Модуле
Pro: Легче отладить код с несколькими меньшими процедурами.
Довод "против": общедоступные массивы (дорогой?)

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

ОБНОВЛЕНИЕ:
Я загружаю Данные учета на дискретном уровне (несколько на Шаг), Данные Перемещений на совокупном уровне (один на шаг), и Начало Материально-технических ресурсов Сдвига на совокупном уровне. Я агрегировал Данные учета шагом, помещающим его в категории Work State (Выполнение, Ожидайте...), я создаю цели от данных уже по листам.

У меня есть Технологическая карта, которая показывает Потоки операций Типом, в настоящее время 3 продукта имеют подобное, но не точно тот же поток, и 2 продуктами является другой поток, которые подобны, но снова не то же друг как друг. Я присвоил каждый набор шагов в различных потоках группа и подгруппа.

Я помещаю эти данные по нескольким листам, некоторым в Порядке Шага, некоторым в группе / порядок подгруппы. Мне также нужны данные, которым подводит итог группа и продукт, группа / подгруппа и продукт, часть строки и продукта и продукта.

Я использую Типы записи, таким образом, у меня на самом деле есть читаемый трехмерный массив, arrSubFlow (1,1) .strStep (Имя шага 1-го Шага 1-го Устройства), arrData (10,5) .lngYest (Вчерашнее перемещение за 10-й Шаг 5-го Устройства).

Мой основной момент оптимизации будет в разделе, где я создаю 10 страниц с нуля каждый раз. С Объединяющимися Ячейками, Границами, Заголовками... Это - очень трудоемкий процесс. Я добавлю раздел, который сравнит мои данные со страницей, чтобы видеть, должно ли это быть изменено и если так, только затем воссоздать его иначе, я очищу каждый раздел данных и только запишу данные, которые изменяются на лист. Это будет огромно, на основе моих данных регистрации времени. Однако каждый раз, когда я обновляю код, я всегда пытаюсь улучшить другие аспекты кода также. Я вижу загрузку данных в Структуру (Массив, RecordSet, Набор) однажды как оба определенная оптимизация, но больше для целостности данных, таким образом, у меня нет возможности загрузить его по-другому для различных листов.

Основные вопросы я вижу уход от Массивов прямо сейчас:
* Уже в большой степени инвестированный в них, но это не достаточно хорошая причина не измениться
* не знают, существует ли очень стоимость для передачи их, так как она будет ByRef
* я использую Функцию Вида для создания Отсортированного массива "Указателя", который позволяет мне оставить Массив в порядке Потока Шага, легко ссылаясь на нее Группой / порядок Подгруппы.

Так как я всегда пытаюсь сделать свой код на данный момент и будущее, я не против обновления массивов или к RecordSets или к Наборам, но не просто ради изменения их, чтобы узнать, что что-то охлаждается. Мои массивы работают и от моего исследования, они добавляют секунды ко времени выполнения, не значительное количество для этого 2-минутного отчета. Таким образом, Если другую структуру легче обновить в будущем, чем Двухмерные антенные решетки Типов записи, то сообщите мне, но кто-либо знает стоимость передачи Массива к процедуре, предполагая, что Вы не делаете передачи ByVal?

7
задан Tim Cooper 24 September 2011 в 13:02
поделиться

2 ответа

Похоже, что Excel и массивы - не самые лучшие инструменты для выполняемой работы. Если бы вы могли немного объяснить, с каким типом данных вы работаете и что вы делаете, это действительно поможет получить лучший ответ. Расскажите как можно подробнее о типах манипуляций с данными и о том, что такое входы и выходы.

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

  • Существует объект, который естественным образом обрабатывает объекты типа Recordset, с которыми вы работаете, называемый Recordset. В редакторе VBA перейдите в Tools -> References (Инструменты -> Ссылки) и добавьте библиотеку Microsoft ActiveX Data Objects 2.X (самая высокая на вашей машине). Вы можете объявить объект типа ADODB.Recordset, затем сделать Recordset.Fields.Append, чтобы добавить к нему поля, затем .Open it и, наконец, .AddNew, установить значения полей, и .Update. Это естественный объект для передачи в программах в качестве входного или выходного параметра. Он имеет естественные функции обхода и позиционирования (.Eof, .Bof, .AbsolutePosition, .MoveNext, .MoveFirst, .MovePrevious) и поддерживает поиск и фильтрацию (.Filter = "Field = 'abc'", .Find и т.д.).

  • Я не рекомендую использовать публичные переменные, хотя без понимания того, что вы делаете, я не могу дать вам хорошего совета.

  • Я бы также избегал одной большой процедуры. Код следует разбивать на многократно используемые функциональные единицы, которые делают только одно, имена которых, по сути, сами документируют то, что они делают.

  • Если вы хотите улучшить производительность вашего кода, ударьте ctrl-брейк в случайное время во время его работы и взломайте код. Затем нажмите Ctrl-L, чтобы просмотреть стек вызовов. Каждый раз запишите, что находится в списке. Если какой-либо пункт отображается большую часть времени, это узкое место, и именно там вы должны потратить свое время, пытаясь оптимизировать его. Однако, я не советую пытаться оптимизировать то, что у вас есть, до тех пор, пока вы не примете некоторые решения более высокого уровня (например, будете ли вы переключаться на набор записей).

Мне действительно нужно больше информации, чтобы помочь вам лучше.

Если вам интересно, я разработаю демонстрационный код, который покажет, насколько полезен объект Recordset. Вставить данные из Recordset в диапазон Excel очень просто с помощью Recordset.GetRows или .GetString (хотя может потребоваться некоторое переложение массива, это тоже не сложно).

UPDATE: Если ваша цель - ускорить процесс, то перед тем, как что-либо делать, я думаю, что лучше всего вооружиться знаниями о том, что занимает больше всего времени. Не могли бы вы, пожалуйста, нажать ctrl-разрыв около 10 раз и каждый раз записывать в стек вызовов, а затем сказать мне, какие наиболее распространенные элементы в стеке вызовов?

С точки зрения обновления скорости форматирования ячеек, вот мой опыт:

  1. Слияние - это самая медленная операция, которую вы можете сделать. Постарайтесь избегать ее, если это вообще возможно. Использование "center across selection" является одной из альтернатив. Другая - это просто не слияние, а использование некоторой комбинации правильного размера, границ, цвета фона ячейки, а также отключение линий сетки для всей рабочей книги.

  2. Применяйте границы или другое форматирование один раз к самому большому размеру, а не к множеству мелких вещей, таких как ячейки за ячейками. Например, если большинство ячеек имеют все границы, а некоторые нет, то применяйте все границы ко всему диапазону и во время петлевания удалите те, которые вам не нужны. И даже тогда, попробуйте сделать целые строки и большие диапазоны.

  3. Сохраните файл-шаблон с уже примененными границами и форматированием. Допустим, вы поместите в него одну строку с форматированием для определенного участка. За один шаг продублируйте эту строку на столько строк, сколько нужно для этого раздела, скажем, 20 строк, и все они будут иметь одинаковое форматирование. Дублирование строк происходит быстрее, чем форматирование по ячейкам.

Кроме того, я бы не стал автоматически использовать классы. Хотя OO - это здорово, и я делаю это сам (черт возьми, я только что построил 8 классов для чего-то на днях, чтобы смоделировать иерархическую структуру, чтобы я мог легко разоблачать ее части, когда они мне понадобятся), на практике это может быть медленнее. Простой набор публичных переменных в классе быстрее, чем использование геттеров и сеттеров. Определенный пользователем тип еще быстрее, чем класс, но можно столкнуться с геттерами, пытающимися передать UDT в классах (они должны быть объявлены в неклассовом публичном модуле, и даже тогда они могут дать проблемы).

Эрик

3
ответ дан 6 December 2019 в 15:22
поделиться

Вы предоставили немного подробнее, но все еще довольно сложно понять, что именно происходит, не видя некоторый код. В вашем вопросе я могу определить как минимум 4 больших тем, которые вы переплетете по всему: производство, доступ к данным, VBA и кодированию лучших практик. Мне трудно рассказать именно то, что вы спрашиваете, потому что ваш вопрос охватывает огромный. В любом случае, я ценю вашу попытую написать лучший код в VBA.

Мне трудно точно понять, что вы планируете делать с массивами. Вы говорите:

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

Я не уверен, что вы имеете в виду здесь. Вы используете массивы, чтобы представлять строку данных, которые вы извлекали из базы данных? Если это так, вы можете рассмотреть возможность использования модулей классов вместо обычных «макроменных» модулей. Они позволят вам работать с полноценными объектами вместо массивов значений (или ссылок, в зависимости от обстоятельств). Классы принимают больше работы для настройки и потребления, но они заставляют ваш код намного проще работать и очень поможет вам сеть свой код.

Как уже отметил, что пользователь Emtucifor уже указан, могут быть такие объекты, как ADO Recordseet объекты (которые могут потребовать доступа для установки ... не уверен), которые могут помочь значительно. Или вы можете создать свой собственный.

Вот длительный пример того, как использование класса может помочь вам. Хотя этот пример длительный, он покажет вам, как несколько принципов объектно-ориентированного программирования действительно могут помочь вам очистить свой код.

В редакторе VBA перейдите в Modeule> Class Module . В окне свойств (по умолчанию внизу левый экран) измените имя модуля на Worklogitem . Добавьте следующий код в класс:

Option Explicit

Private pTaskID As Long
Private pPersonName As String
Private pHoursWorked As Double

Public Property Get TaskID() As Long
    TaskID = pTaskID
End Property

Public Property Let TaskID(lTaskID As Long)
    pTaskID = lTaskID
End Property

Public Property Get PersonName() As String
    PersonName = pPersonName
End Property

Public Property Let PersonName(lPersonName As String)
    pPersonName = lPersonName
End Property

Public Property Get HoursWorked() As Double
    HoursWorked = pHoursWorked
End Property

Public Property Let HoursWorked(lHoursWorked As Double)
    pHoursWorked = lHoursWorked
End Property

Приведенный выше код даст нам сильно напечатанный объект, который специфичен для данных, с которыми мы работаем. Когда вы используете многоразмерные массивы для хранения данных, ваш код напоминает это: ART (1,1) - это идентификатор, ARR (1,2) - это персонал, и ARR (1,3) - это работа. Используя этот синтаксис, трудно знать, что такое. Предположим, вы все еще загружаете свои объекты в массив, но вместо этого используете WorkLogitem , который мы создали выше. Это имя, вы сможете сделать ARM (1) .Personname , чтобы получить имя человека. Это делает ваш код намного проще прочитать.

Давайте продолжим двигаться с этим примером. Вместо того, чтобы сохранить объекты в массиве, мы попробуем использовать коллекцию .

Далее добавьте новый класс модуля и вызовите его Processworklog . Поместите следующий код:

Option Explicit

Private pWorkLogItems As Collection

Public Property Get WorkLogItems() As Collection
    Set WorkLogItems = pWorkLogItems
End Property

Public Property Set WorkLogItems(lWorkLogItem As Collection)
    Set pWorkLogItems = lWorkLogItem
End Property

Function GetHoursWorked(strPersonName As String) As Double
    On Error GoTo Handle_Errors
    Dim wli As WorkLogItem
    Dim doubleTotal As Double
    doubleTotal = 0
    For Each wli In WorkLogItems
        If strPersonName = wli.PersonName Then
            doubleTotal = doubleTotal + wli.HoursWorked
        End If
    Next wli

Exit_Here:
    GetHoursWorked = doubleTotal
        Exit Function

Handle_Errors:
        'You will probably want to catch the error that will '
        'occur if WorkLogItems has not been set '
        Resume Exit_Here


End Function

Вышеуказанный класс будет использоваться для «сделать что-то» с коллектором работы Worklogitem . Первоначально мы просто устанавливаем его, чтобы рассчитывать на общее количество работающих часов. Давайте проверим код, который мы написали. Создайте новый модуль (не класс модуля на этот раз; просто «регулярный» модуль). Вставьте следующий код в модуле:

Option Explicit

Function PopulateArray() As Collection
    Dim clnWlis As Collection
    Dim wli As WorkLogItem
    'Put some data in the collection'
    Set clnWlis = New Collection

    Set wli = New WorkLogItem
    wli.TaskID = 1
    wli.PersonName = "Fred"
    wli.HoursWorked = 4.5
    clnWlis.Add wli

    Set wli = New WorkLogItem
    wli.TaskID = 2
    wli.PersonName = "Sally"
    wli.HoursWorked = 3
    clnWlis.Add wli

    Set wli = New WorkLogItem
    wli.TaskID = 3
    wli.PersonName = "Fred"
    wli.HoursWorked = 2.5
    clnWlis.Add wli

    Set PopulateArray = clnWlis
End Function

Sub TestGetHoursWorked()
    Dim pwl As ProcessWorkLog
    Dim arrWli() As WorkLogItem
    Set pwl = New ProcessWorkLog
    Set pwl.WorkLogItems = PopulateArray()
    Debug.Print pwl.GetHoursWorked("Fred")

End Sub

в вышеуказанном коде populatearray () просто создает коллекцию Worklogitem . В вашем реальном коде вы можете создать класс для анализа листов Excel или объекты данных для заполнения коллекции или массива.

Код TestGethoursWorked () код просто демонстрирует, как использовались классы. Вы замечаете, что Processworklog создается в качестве объекта. После того, как он создается, коллекция Worklogitem становится частью объекта PWL . Вы замечаете это в строке SET PWL.Worklogtems = PopulatearRay () . Далее мы просто называем функцию, которую мы написали, которая действует на коллекцию Worklogtrogitems .

Почему это полезно?

Давайте предположим, что ваши изменения данных, и вы хотите добавить новый метод. Предположим, ваш Worklogitem теперь включает в себя поле для TOONOk , и вы хотите добавить новый метод для расчета этого.

Все, что вам нужно сделать, это добавить свойство в Worklogitem , как так:

Private pHoursOnBreak As Double

Public Property Get HoursOnBreak() As Double
    HoursOnBreak = pHoursOnBreak
End Property

Public Property Let HoursOnBreak(lHoursOnBreak As Double)
    pHoursOnBreak = lHoursOnBreak
End Property

, конечно, вам нужно изменить свой способ заполнения вашей коллекции (используемый мной метод образца Populatearray () , но вы, вероятно, должны иметь отдельный класс только для этого). Тогда вы просто добавляете свой новый метод в свой ChangeWorklog класс:

Function GetHoursOnBreak(strPersonName As String) As Double
     'Code to get hours on break
End Function

Теперь, если мы хотели обновить наши TestGethoursWorked () метод возврата результата Gethoursonbreak ,Все, что нам было бы сделать, чтобы добавить следующую строку:

    Debug.Print pwl.GetHoursOnBreak("Fred")

Если вы проходили в массиве значений, которые представляли свои данные, вам придется найти каждое место в вашем коде, где вы использовали массивы, а затем обновите его соответственно. Если вы используете классы (и их экземпляры) вместо этого, вы можете намного легче обновлять свой код для работы с изменениями. Кроме того, когда вы позволяете использовать класс по нескольким способам (возможно, одна функция требует только 4 объектов свойств, когда другая функция потребуется 6), они все еще могут ссылаться на тот же объект. Это удерживает вас от нескольких массивов для различных типов функций.

Для дальнейшего чтения я бы очень рекомендую получить копию справочника VBA, 2-е издание . Книга полна отличных примеров и лучших практик и тонн кода образца. Если вы инвестируете много времени на VBA для серьезного проекта, он стоит ваше время посмотреть в эту книгу.

11
ответ дан 6 December 2019 в 15:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: