Ошибка при поиске последней использованной ячейки в Excel с VBA

179

Когда я хочу найти значение последней использованной ячейки, я использую:

Dim LastRow As Long

LastRow = Range("E4:E48").End(xlDown).Row

Debug.Print LastRow

Я получаю неправильный вывод, когда я помещаю один элемент в ячейку. Но когда я помещаю более одного значения в ячейку, вывод правильный. В чем причина этого?

Джеймс
источник

Ответы:

309

ПРИМЕЧАНИЕ : я намерен сделать это "одним постом", где вы можете использовать Correctспособ, чтобы найти последний ряд. Это также охватит лучшие практики, которым нужно следовать при поиске последней строки. И поэтому я буду обновлять его всякий раз, когда сталкиваюсь с новым сценарием / информацией.


Ненадежные способы поиска последнего ряда

Некоторые из наиболее распространенных способов поиска последней строки, которые крайне ненадежны и, следовательно, никогда не должны использоваться.

  1. UsedRange
  2. xlDown
  3. COUNTA

UsedRangeдолжны НИКОГДА быть использованы , чтобы найти последнюю ячейку , которая имеет данные. Это очень ненадежно. Попробуйте этот эксперимент.

Введите что-то в клетку A5. Теперь, когда вы вычислите последнюю строку любым из приведенных ниже методов, вы получите 5. Теперь закрасьте ячейку A10красным. Если вы сейчас используете любой из приведенного ниже кода, вы все равно получите 5. Если вы используете Usedrange.Rows.Countто, что вы получаете? Это не будет 5.

Вот сценарий, чтобы показать, как UsedRangeработает.

введите описание изображения здесь

xlDown одинаково ненадежен.

Рассмотрим этот код

lastrow = Range("A1").End(xlDown).Row

Что бы произошло, если бы была только одна ячейка ( A1) с данными? В конечном итоге вы достигнете последней строки в таблице! Это как выбрать ячейку, A1а затем нажать Endклавишу и затем нажать Down Arrowклавишу. Это также даст вам ненадежные результаты, если в диапазоне есть пустые ячейки.

CountA также ненадежен, потому что он даст вам неправильный результат, если между ними есть пустые ячейки.

И , следовательно , следует избегать использования UsedRange, xlDownи CountAнайти последнюю ячейку.


Найти последнюю строку в столбце

Чтобы найти последнюю строку в Col E, используйте эту

With Sheets("Sheet1")
    LastRow = .Range("E" & .Rows.Count).End(xlUp).Row
End With

Если вы заметили, что у нас есть .раньше Rows.Count. Мы часто предпочитали игнорировать это. Смотрите этот вопрос о возможной ошибке, которую вы можете получить. Я всегда советую использовать .до Rows.Countи Columns.Count. Этот вопрос является классическим сценарием, в котором код не работает, потому что Rows.Countвозвращается 65536для Excel 2003 и более ранних версий и 1048576для Excel 2007 и более поздних версий. Аналогично Columns.Countвозвращает 256и 16384, соответственно.

Приведенный выше факт, что в Excel 2007+ есть 1048576строки, также подчеркивает тот факт, что мы всегда должны объявлять переменную, которая будет содержать значение строки, поскольку Longв Integerпротивном случае вы получите Overflowошибку.

Обратите внимание, что этот подход будет пропускать любые скрытые строки. Оглядываясь на мой скриншот выше для столбца A , если строка 8 была скрыта, этот подход вернется 5вместо 8.


Найти последнюю строку в листе

Чтобы найти Effectiveпоследнюю строку в листе, используйте это. Обратите внимание на использование Application.WorksheetFunction.CountA(.Cells). Это необходимо, потому что если в рабочем листе нет ячеек с данными, то .Findвы получитеRun Time Error 91: Object Variable or With block variable not set

With Sheets("Sheet1")
    If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
        lastrow = .Cells.Find(What:="*", _
                      After:=.Range("A1"), _
                      Lookat:=xlPart, _
                      LookIn:=xlFormulas, _
                      SearchOrder:=xlByRows, _
                      SearchDirection:=xlPrevious, _
                      MatchCase:=False).Row
    Else
        lastrow = 1
    End If
End With

Найти последнюю строку в таблице (ListObject)

Применяются те же принципы, например, для получения последней строки в третьем столбце таблицы:

Sub FindLastRowInExcelTableColAandB()
Dim lastRow As Long
Dim ws As Worksheet, tbl as ListObject
Set ws = Sheets("Sheet1")  'Modify as needed
'Assuming the name of the table is "Table1", modify as needed
Set tbl = ws.ListObjects("Table1")

With tbl.ListColumns(3).Range
    lastrow = .Find(What:="*", _
                After:=.Cells(1), _
                Lookat:=xlPart, _
                LookIn:=xlFormulas, _
                SearchOrder:=xlByRows, _
                SearchDirection:=xlPrevious, _
                MatchCase:=False).Row
End With

End Sub
Сиддхарт Рут
источник
9
@phan: Введите что-нибудь в ячейку A5. Теперь, когда вы вычислите последнюю строку любым из методов, описанных выше, вы получите 5. Теперь закрасьте ячейку A10 красным. Если вы сейчас используете любой из приведенного выше кода, вы все равно получите 5. Если вы используете Usedrange.Rows.Countто, что вы получаете? Это не будет 5. Usedrange крайне ненадежен, чтобы найти последний ряд.
Сиддхарт Рут
6
Обратите внимание, что .Find, к сожалению, портит настройки пользователя в диалоговом окне «Найти», т. Е. В Excel есть только 1 набор настроек для диалога, и вы используете .ind заменяет их. Другой трюк заключается в том, чтобы по-прежнему использовать UsedRange, но использовать его как абсолютный (но ненадежный) максимум, из которого вы определяете правильный максимум.
Карл Колейн
4
@CarlColijn: я бы не назвал это безобразием. :) Excel просто remembersпоследняя настройка. Даже когда вы вручную делаете a Find, он запоминает последний параметр, который на самом деле является благом, если кто-то знает этот «факт»
Siddharth Rout
3
@KeithPark: Пожалуйста, продолжайте :) Знание имеет смысл, только если оно распространяется :)
Сиддхарт Рут
9
Я думаю, что ваше описание UsedRange( крайне ненадежно найти последнюю ячейку, в которой есть данные ) вводит в заблуждение. UsedRangeпросто не предназначен для этой цели, хотя в некоторых случаях это может дать правильный результат. Я думаю, что предложенный эксперимент добавляет путаницы. Результат, полученный с помощью UsedRange($ A $ 1: $ A $ 8), не зависит от первого ввода данных или их удаления. Цифра справа останется прежней, даже если данные не были удалены. Пожалуйста, посмотрите мой ответ.
sancho.s ReinstateMonicaCellio