#excel #vba #crash #copy #copy-paste
Вопрос:
Я несколько раз искал решение для использования .PasteSpecial в макросе excel vba. У меня есть отчеты, которые иногда могут содержать тысячи строк. В этих отчетах два столбца, мне нужно объединить эти столбцы, чтобы, если в одном столбце есть пробелы, я хотел бы получить значение из второго столбца; в противном случае просто сохраните значение в первом столбце. Мне нужно применить это дважды в двух разных местах. Все это вложено в более крупный код.
Мое решение-использовать .PasteSpecial с «Пропуском заготовок». Это быстро обрабатывается Excel, намного быстрее, чем циклическое выполнение строки за строкой. Проблема в том, что код продолжает выводить excel из строя.
После отладки вот что я узнал до сих пор: *Первый .PasteSpecial всегда работает, но когда дело доходит до второго .По-особому это всегда терпит неудачу. *Я попробовал ОСТАНОВИТЬСЯ после первого .PasteSpecial, затем пройти через код, а затем пройти через второй.Специальный код работает просто отлично. *Если я пройду через второй .В прошлом это работает так, как будто ничего не случилось, но если я просто запущу код как обычно, он выйдет из строя. *Я поменял порядок двух .PasteSpecials в коде. Когда я это делаю, он больше не падает на проблемном .Специальная паста, но она выходит из строя на первоначально работающем .специальная паста.
Исходя из этого, я знаю, что проблема в том, что Excel это не нравится .Специальная вставка дважды в коде. Все еще не могу найти обходной путь. Я попытался очистить доску для клипов, и я недостаточно знаю, как настроить массив, не говоря уже о том, эффективно ли это для такого количества данных. Кто — нибудь знает решение или обходной путь?
Вот мой .специальный код.:
MainSheet.Range("N:N").Copy
MainSheet.Range("P:P").PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:=True, Transpose:=False
MainSheet.Range("R:R").Copy
MainSheet.Range("Q:Q").PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:=True, Transpose:=False
Комментарии:
1. Что произойдет, если вы попытаетесь скопировать / вставить специальное вручную?
2. Вы пробовали
Application.CutCopyMode=False
переключаться между двумя операциями копирования/вставки?3. Вам тоже нужно скопировать формат?
4. Если я попытаюсь сделать это вручную, это сработает просто отлично. Я попробовал приложение. CutCopyMode=False, и он все равно вылетает. Мне тоже не нужно форматирование.
Ответ №1:
Ваша проблема в том, что вы выделяете процессор для выполнения задач на уровне ОС, когда на самом деле вам ничего не нужно вставлять.
В ячейках есть значения… так сделай их равными и они… будет… быть…
Range("C1").Value = Range("A1").Value
В качестве альтернативы вы можете использовать запрос мощности, чтобы просто сделать это, где таблица 2 Col2 равна нулю, а ID = ID
Комментарии:
1. Привет, спасибо за ответ, но это не соответствует моей цели. Чтобы уточнить мою цель: причина, по которой я использую .PasteSpecial, состоит в том, чтобы «Пропустить пробелы», чтобы, если в первом столбце есть пробелы, а во втором столбце (в той же строке) есть текст, он НЕ стирал текст второго столбца. Однако, если в обоих есть текст, текст первого столбца переопределяет текст второго столбца.
Ответ №2:
Не нуждаясь в формате, пожалуйста, протестируйте следующий код. Он использует массив, а не буфер обмена и работает быстрее. Если нет необходимости в существовании соответствия между строками в двух заполненных столбцах, вы можете воспользоваться следующим быстрым способом:
Sub copyColumnsArray()
Dim lastR As Long, arrCopy, arrFin, i As Long, k As Long
lastR = MainSheet.Range("N" amp; rows.count).End(xlUp).row
arr = MainSheet.Range("N1:N" amp; lastR).value
'fill another array only with non empty values:__________________
ReDim arrFin(UBound(arr) To 1): k = 1
For i = 1 To UBound(arrCopy)
If arrCopy(i, 1) <> "" Then arrFin(k, 1) = arrCopy(i, 1): k = k 1
Next i
ReDim Preserve arrFin(k - 1 To 1)
'______________________________________________________
MainSheet.Range("P1").Resize(UBound(arrFin), 1).value = arrFin
lastR = MainSheet.Range("R" amp; rows.count).End(xlUp).row
arr = MainSheet.Range("R1:R" amp; lastR).value
'fill another array only with non empty values:__________________
ReDim arrFin(UBound(arr) To 1): k = 1
For i = 1 To UBound(arrCopy)
If arrCopy(i, 1) <> "" Then arrFin(k, 1) = arrCopy(i, 1): k = k 1
Next i
ReDim Preserve arrFin(k - 1 To 1)
'______________________________________________________
MainSheet.Range("Q1").Resize(UBound(arrFin), 1).value = arrFin
End Sub
Комментарии:
1. Привет, спасибо за ответ, но это не работает. После запуска кода он заполняет пробелы из первого столбца во второй столбец. Чтобы уточнить мою цель: причина, по которой я использую .PasteSpecial, состоит в том, чтобы «Пропустить пробелы», чтобы, если в первом столбце есть пробелы, а во втором столбце (в той же строке) есть текст, он НЕ стирал текст второго столбца. Однако, если в обоих есть текст, текст первого столбца переопределяет текст второго столбца.
2. @Стивен, пожалуйста, попробуйте обновленный код. Я использовал (по ошибке)
arr
, даже не объявленный, вместоarrCopy
. Сначала я протестировал код с помощьюarr
, а затем подумал, что использованиеarrCopy
будет более выразительным… ИспользованиеSkipBlanks
в последовательных шагах может создать проблемы, если пустые ячейки не существуют в одной строке…3. Я заметил, что arr и arrCopy отключены — я подумал, что, вероятно, вы это имели в виду, так что не беспокойтесь. Я думаю, что проблема с использованием метода массива заключается в том, что при построении массива он помещает «» внутри соответствующих индексов, поэтому, когда он изменяет значение второго столбца, он каждый раз помещает «» вместо того, чтобы пропускать пробел. Я пытаюсь поиграть с такими идеями, как «Если значение, если arrCopy = «» тогда» пропустите еще … » Можете ли вы помочь коду, чтобы он пропускал ввод значения массива, если оно пустое?
4. Я нашел решение и ввел его в ваш пост, чтобы отметить его как правильный ответ. После того, как правка будет одобрена, я отмечу ее как правильный ответ. Спасибо за вашу помощь!
5. @Стивен, если необходимо пропустить пустые ячейки, я адаптирую код, но не так, как вы пытались. Больше всего времени и ресурсов уходит на вставку значения в ячейку. если корреляция между строками двух столбцов не имеет значения, я создам второй массив, чтобы получать только не пустые ячейки и сразу удалять его содержимое.
Ответ №3:
Редактировать:
Лучший ответ, который работал более надежно, чем мое оригинальное найденное решение, приведен ниже. Это адаптация некоторых советов из ответа ФанеДуру. Это решение более требовательно к ресурсам; однако на данный момент — оно выполняет задачу надежно (без сбоев). Я бы хотел, чтобы были лучшие ответы, чем циклическое повторение строк; однако это действительно отвечает на мою операцию. Спасибо вам за всю помощь!
Sub copyColumnsArray()
Dim lastR As Long, arrCopy
lastR = MainSheet.Range("N" amp; rows.count).End(xlUp).row
arrCopy = MainSheet.Range("N1:N" amp; lastR).value
Dim ArrayIndex as Variant
Dim RowCount as String
RowCount = 1
For Each ArrayIndex in arrCopy
If ArrayIndex = "" then
RowCount = RowCount 1
'Skip Blank
else
MainSheet.Range("P" RowCount).value = ArrayIndex
RowCount = RowCount 1
end if
Next
lastR = MainSheet.Range("R" amp; rows.count).End(xlUp).row
arrCopy = MainSheet.Range("R1:R" amp; lastR).value
RowCount = 1
For Each ArrayIndex in arrCopy
If ArrayIndex = "" then
RowCount = RowCount 1
'Skip Blank
else
MainSheet.Range("Q" RowCount).value = ArrayIndex
RowCount = RowCount 1
end if
Next
End Sub
Комментарии:
1. Похоже, это вопрос необходимых ресурсов Excel, которые пытались вставить до завершения предыдущего процесса вставки. Это ненормальное поведение, VBA должен ждать сам по себе, пока операция не завершится. Лучшим решением должно быть использование массивов, которые вообще не используют буфер обмена и потребляют гораздо меньше ресурсов Excel. Я опубликовал фрагмент кода, способный заменить ваш, более стабильный и быстрый.