Одним из способов повышения производительности является уменьшение взаимодействия VBA с объектами Excel, поэтому вполне понятно, что циклы могут сильно повлиять на производительность.
Давайте посмотрим на ваши три цикла.
L oop 1
For i = 1 To masterTbl.ListRows.Count
Это основной l oop в вашем алгоритме, и он, вероятно, проходит по неверной таблице. Если обе таблицы имеют одинаковый размер, это не имеет значения; однако, если мастер больше, чем импорт (как это часто бывает), то вы без необходимости зацикливаетесь на строках в мастере, которые не могут существовать при импорте.
L oop 2
importTbl.ListColumns(1).Range.Find(IC, LookAt:=xlWhole)
Многие люди упускают из виду простой факт, что Find
- это всего лишь oop, и он особенно неэффективен, когда вложен в For
l oop. Рассмотрим For
l oop, который перебирает таблицу с 500 строками: Find
будет вызываться 500 раз, а первая строка - 500 раз. Если все 500 строк содержат совпадение, то Find
потребуется только 125 125 оценок, чтобы найти все 500. Теперь держитесь за свое место, если есть только одно совпадение, и оно находится в 500-м ряду, Find
сделает 249,499 оценок, прежде чем найти его на 249,500-й. А если нет совпадений? Все 500 строк будут оценены 500 раз, ошеломляющие 250 000 оценок! Давайте не будем забывать, что это все оценки на рабочем листе, то, что вы хотите минимизировать.
L oop 3
For j = 2 To adminTbl.ListColumns.Count
Это самый внутренний l oop и тот, кто отвечает за выполнение реальной работы. Здесь есть несколько проблем, которые я расскажу отдельно.
Если HeaderRowRange
не изменится во время выполнения, тогда вы можете избежать повторных ссылок на importTbl.HeaderRowRange.Row
и ускорить процесс (хотя и незаметно), присваивая номер строки переменной над вашими циклами. Затем вы будете использовать эту переменную везде, где вам это нужно.
Работа по ячейкам также замедляет работу. Вы должны читать по одной ячейке за раз, но вам не нужно читать каждую ячейку. Нахождения одного изменения достаточно, чтобы обновить залог строки таблицы из l oop с помощью Exit For
.
Это много раздумий, которые включают много переписанного кода , но это не то, что я на самом деле делаю. Я бы взял go для низко висящих фруктов, используя фильтр, который уменьшает размер внешнего l oop и устраняет Find
.
. Нам нужен массив для фильтра. Это очень быстрое короткое l oop отлично работает:
Dim aryID() as String
ReDim aryID(1 to masterTbl.ListRows.Count)
For i = 1 To UBound(aryID)
'grabs ID
aryID(I) = masterTbl.ListColumns(1).DataBodyRange(i).Value2
Next i
Нам нужно назначить фильтр, и мы можем сделать это с одним вкладышем, фильтры могут быть хитрыми, потому что текущий фильтр может быть активным, неактивным, или отсутствует. Поэтому я обычно передаю таблицу в подпрограмму, чтобы сбросить фильтр в известное состояние, прежде чем установить критерии:
ResetTableFilters masterTbl
masterTbl.Range.AutoFilter Field:=1, Criteria1:=arryID Operator:=xlFilterValues
Теоретически, вы должны иметь возможность скопировать и вставить это в текущий модуль и запустить Это. Но это написано на телефоне и не было проверено
Sub ResetTableFilters(lo As ListObject)
If lo.ShowAutoFilter Then lo.DataBodyRange.AutoFilter
lo.DataBodyRange.AutoFilter
End Sub
Sub detectChanges(adminTbl As ListObject, importTbl As ListObject, masterTbl As ListObject)
Dim i As Long
Dim j As Long
Dim elements As Long
Dim Header As Variant
Dim foundHeader As Variant
Dim cHead As String
Dim ID As Variant
Dim foundID As Range
elements = 0
'loops through the set admin headers. This format will not copy non-similar data, nor overwrite custom columns that have been added to the data
'NOTE: Below assumed that unique identifier is ALWAYS in first column.
Dim aryID() as String
ReDim aryID(1 to masterTbl.ListRows.Count)
For i = 1 To UBound(aryID)
'grabs ID
aryID(I) = masterTbl.ListColumns(1).DataBodyRange(i).Value2
Next i
For i = 1 To masterTbl.ListColumns(1).DataBodyRange(i).SpecialCells(xlCellTypeVisible).Count
'checks for changes in admin columns, skipping the ID column
For j = 2 To adminTbl.ListColumns.Count
cHead = adminTbl.ListColumns(j).Name
If masterTbl.ListColumns(cHead).DataBodyRange(i) <>
importTbl.ListColumns(cHead).DataBodyRange(foundID.Row - importTbl.HeaderRowRange.Row) Then
'This line changes the color of the changed element
importTbl.ListColumns(cHead).DataBodyRange(foundID.Row - importTbl.HeaderRowRange.Row).Copy masterTbl.ListColumns(cHead).DataBodyRange(i)
masterTbl.ListColumns(cHead).DataBodyRange(i).Interior.Color = RGB(255, 235, 156)
'keeping track of elements
elements = elements + 1
End If
Next j
End If
Next i
'Say elements changed
MsgBox "Total elements changed in update: " & elements
End Sub