#excel #vba
#excel #vba
Вопрос:
Я получаю сообщение об ошибке как ошибка времени выполнения 7 при запуске утилиты очистки имен, как указано в приведенном ниже коде. Я использую жесткий диск объемом 512 ГБ, 8 ГБ оперативной памяти, процессор I7, поэтому проблем с памятью не должно быть, и все же проблема возникает.
В моей книге определено 123188 имен, которые я хочу удалить, используя приведенный ниже код. Есть ли способ повысить эффективность работы с кодом / у кого-нибудь есть код / встроенная надстройка, которую я могу включить в основную надстройку?
Функция прерывается при
Для каждого objName в ActiveWorkbook.Имена
Любая помощь будет оценена.
Заранее спасибо
Option Explicit
Sub Cleanup_names123()
'
'Deletes all names except for Print_Area, Database, and DB
'Declare variables
Dim objName As Name
Dim strAnswer As String
'Display instructions
strAnswer = MsgBox("This function will delete all named ranges except Print_Area, DB, and Database. If you are not ready to proceed click Cancel to exit.", vbOKCancel)
'If cancelled - exit function
If strAnswer = vbCancel Then End
'If no names found, exit
If ActiveWorkbook.Names.Count = 0 Then
MsgBox "No names found. Macro complete."
End
End If
MsgBox ActiveWorkbook.Names.Count amp; " name(s) found. It may take a few minutes for the cleanup."
'Delete names
For Each objName In ActiveWorkbook.Names
On Error Resume Next
If InStr(objName.Name, "Database") <> 0 Then
'If Database - no action
ElseIf InStr(objName.Name, "database") <> 0 Then
'If database - no action
ElseIf InStr(objName.Name, "DB") <> 0 Then
'If database - no action
Else
objName.Delete
ThisWorkbook.Names(objName.Name).Delete
End If
Next
On Error GoTo 0
End Sub
Комментарии:
1. Из того, что я до сих пор искал в Google, в нем упоминается, что, поскольку ему приходится перебирать множество имен, это может быть неэффективно и может привести к сбою. Альтернативой, которую предлагает Google, является сохранение имен в массиве, а затем их немедленное удаление. Любые предложения, если это хорошая идея и как это можно сделать?
Ответ №1:
Если итерация коллекции занимает слишком много памяти, вы можете вручную выбрать каждый элемент по одному. При удалении элементов важно работать в обратном направлении с конца, потому что при удалении элемента 1 элемент 2 становится элементом 1. Поэтому мы используем Step -1
для работы в обратном направлении.
Чтобы сделать ваше предложение guard понятным и избежать пустых Ifs, я изменил логику на If Not And
. Я нахожу это более понятным. Не используйте подчеркивание _
в именах методов, поскольку оно зарезервировано для методов событий.
Option Explicit
Public Sub CleanupNames()
'
'Deletes all names except for Print_Area, Database, and DB
'Declare variables
Dim strAnswer As String
'Display instructions
strAnswer = MsgBox("This function will delete all named ranges except Print_Area, DB, and Database. If you are not ready to proceed click Cancel to exit.", vbOKCancel)
'If cancelled - exit function
If strAnswer = vbCancel Then Exit Sub
Dim NamesCount As Long
NamesCount = ActiveWorkbook.Names.Count
'If no names found, exit
If NamesCount = 0 Then
MsgBox "No names found. Macro complete."
Exit Sub
End If
MsgBox NamesCount amp; " name(s) found. It may take a few minutes for the cleanup."
'Delete names
Dim iter As Long
For iter = NamesCount To 1 Step -1
Dim objName As String
objName = ActiveWorkbook.Names.Item(iter).Name
On Error Resume Next
If Not InStr(objName, "Database") <> 0 And _
Not InStr(objName, "database") <> 0 And _
Not InStr(objName, "DB") <> 0 Then
ActiveWorkbook.Names(objName).Delete
End If
If iter Mod 5000 = 0 Then ActiveWorkbook.Save
Next iter
End Sub
ОБНОВЛЕНИЕ: добавлен код сохранения и изменено поведение удаления.
Комментарии:
1. благодарим вас за изменение кода. Я запустил код, и теперь он работает. Быстрый вопрос, код удалил 16473 имени за час. Это должно быть так медленно? Мой размер файла составляет всего 5 МБ, и я тоже использую 64-разрядную версию. Я спрашиваю об этом, поскольку код может аварийно завершиться, если он будет работать слишком долго. Случайно, мы можем добавить функцию, которая после каждого, возможно, удаления имен 5k код сохраняет файл и запускается повторно, чтобы избежать сбоя?
2. Внутри цикла for используйте Mod (модули). Если Iter Mod 5000 = 0, то ActiveWorkbook.Save
3. Привет, спасибо за это. Я пытался, но не удалось. Можете ли вы помочь опубликовать код здесь, чтобы я мог скопировать код. Также, чтобы подтвердить, должно ли удаление диапазонов имен быть такой медленной задачей?
4. Я изменил код, чтобы использовать только имя в качестве строки вместо объекта. Это может немного ускорить процесс. Я также добавил код для сохранения каждых 5000 записей, и это, безусловно, сильно замедлит его.
Ответ №2:
Попробуйте это. Подход, который я использовал, заключался в том, чтобы запускать цикл в обратном направлении снизу, чтобы Excel не перемещал элементы вверх каждый раз, когда они удаляются и удаляются через номер индекса, а не имя. Я также сделал тест немного более эффективным, я думаю.
Option Explicit
Sub DeleteNames()
Dim NameCount As Long
Dim Cntr As Long
Dim WkBk As Workbook
Dim TestName As String
Set WkBk = ThisWorkbook
NameCount = ActiveWorkbook.Names.Count
'Delete names
With WkBk
For Cntr = NameCount To 1 Step -1
On Error Resume Next 'not sure you need this but can't hurt
TestName = UCase(.Names(Cntr).NameLocal)
If InStr(TestName, "DATABASE") > 0 Or _
InStr(TestName, "DB") > 0 Then
'If database - no action
Else
.Names(Cntr).Delete
End If
Next Cntr
End With 'WkBk
End Sub
HTH
Комментарии:
1. Привет, спасибо за код. Я также попробовал это в имеющейся у меня книге, и, похоже, она имеет ту же скорость, что и предыдущая. (Хотя это работает). Правда ли, что код удаляет 300 имен в минуту? Как и в этом случае, удаление имен займет много времени. В моей книге более 125000 имен
Ответ №3:
Это может быть так же просто, как отключить вычисления и обновления экрана… это должно повысить стабильность и значительно увеличить скорость.
Итак, перед вашим For…Следующий цикл —
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Затем после вашего For …Следующий цикл —
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Вам также следует рассмотреть возможность запуска этих последних двух строк после отслеживания любых ошибок, чтобы не оставлять настройки неактивными.
Комментарии:
1. Я сомневаюсь, что это поможет с ошибкой, связанной с памятью.
2. @dwirony, xlCalculationManual — это проверенный временем метод помощи при ошибках нехватки памяти в Excel. Это, безусловно, стоит попробовать. Я собирался опубликовать ссылку, но их так много.
3. Я тоже читал этот подход где-то еще, но по какой-то причине он не сработал. 🙂
Ответ №4:
Вот дополнительная информация о сохранении, которую вы просили. Я не закодировал ваше решение, поскольку вы должны иметь возможность соответствующим образом изменить это. Это отсчитывается назад от 50 и сохраняется каждые 5 раз в цикле с помощью инструкции debug, чтобы вы могли видеть, что это работает.
Sub quickSaveDemo()
Dim counter As Integer
counter = 50
For i = counter To 1 Step -1
Debug.Print "Loop count - " amp; i amp; " - Other stuff here"
If i Mod 5 = 0 Then
Debug.Print "Save here"
ActiveWorkbook.Save
End If
Next i
End Sub