data with column excel Error in finding last used cell in VBA
As to the correct way of finding the last used cell, one has first to decide what is considered used, and then select a suitable method. I conceive at least three meanings:
Used = non-blank, i.e., having data.
Used = "... in use, meaning the section that contains data or formatting." As per official documentation, this is the criterion used by Excel at the time of saving. See also this. If one is not aware of this, the criterion may produce unexpected results, but it may also be intentionally exploited (less often, surely), e.g., to highlight or print specific regions, which may eventually have no data. And, of course, it is desirable as a criterion for the range to use when saving a workbook, lest losing part of one's work.
Used = "... in use, meaning the section that contains data or formatting" or conditional formatting. Same as 2., but also including cells that are the target for any Conditional Formatting rule.
How to find the last used cell depends on what you want (your criterion).
For criterion 1, I suggest reading this answer.
UsedRange is cited as unreliable. I think that is misleading (i.e., "unfair" to
UsedRange is simply not meant to report the last cell containing data. So it should not be used in this case, as indicated in that answer. See also this comment.
For criterion 2,
UsedRange is the most reliable option, as compared to other options also designed for this use. It even makes it unnecessary to save a workbook to make sure that the last cell is updated.
Ctrl+End will go to a wrong cell prior to saving
(“The last cell is not reset until you save the worksheet”, from
It is an old reference, but in this respect valid).
For criterion 3, I do not know any built-in method.
Criterion 2 does not account for Conditional Formatting. One may have formatted cells, based on formulas, which are not detected by
UsedRange or Ctrl+End.
In the figure, the last cell is B3, since formatting was applied explicitly to it. Cells B6:D7 have a format derived from a Conditional Formatting rule, and this is not detected even by
Accounting for this would require some VBA programming.
As to your specific question: What's the reason behind this?
Your code uses the first cell in your range E4:E48 as a trampoline, for jumping down with
The "erroneous" output will obtain if there are no non-blank cells in your range other than perhaps the first. Then, you are leaping in the dark, i.e., down the worksheet (you should note the difference between blank and empty string!).
If your range contains non-contiguous non-blank cells, then it will also give a wrong result.
If there is only one non-blank cell, but it is not the first one, your code will still give you the correct result.
When I want to find the last used cell value, I use:
Dim LastRow As Long LastRow = Range("E4:E48").End(xlDown).Row Debug.Print LastRow
I am getting the wrong output when I put a single element into a cell. But when I put more than one value into the cell, the output is correct. What's the reason behind this?
I would add to the answer given by Siddarth Rout to say that the CountA call can be skipped by having Find return a Range object, instead of a row number, and then test the returned Range object to see if it is Nothing (blank worksheet).
Also, I would have my version of any LastRow procedure return a zero for a blank worksheet, then I can know it is blank.
I wonder that nobody has mentioned this, But the easiest way of getting the last used cell is:
Function GetLastCell(sh as Worksheet) As Range GetLastCell = sh.Cells(1,1).SpecialCells(xlLastCell) End Function
This essentially returns the same cell that you get by Ctrl + End after selecting Cell
A word of caution: Excel keeps track of the most bottom-right cell that was ever used in a worksheet. So if for example you enter something in B3 and something else in H8 and then later on delete the contents of H8, pressing Ctrl + End will still take you to H8 cell. The above function will have the same behavior.
Since the original question is about problems with finding the last cell, in this answer I will list the various ways you can get unexpected results; see my answer to "How can I find last row that contains data in the Excel sheet with a macro?" for my take on solving this.
[...] one has first to decide what is considered used. I see at least 6 meanings. Cell has:
- 1) data, i.e., a formula, possibly resulting in a blank value;
- 2) a value, i.e., a non-blank formula or constant;
- 3) formatting;
- 4) conditional formatting;
- 5) a shape (including Comment) overlapping the cell;
- 6) involvement in a Table (List Object).
Which combination do you want to test for? Some (such as Tables) may be more difficult to test for, and some may be rare (such as a shape outside of data range), but others may vary based on the situation (e.g., formulas with blank values).
Other things you might want to consider:
- A) Can there be hidden rows (e.g. autofilter), blank cells or blank rows?
- B) What kind of performance is acceptable?
- C) Can the VBA macro affect the workbook or the application settings in any way?
With that in mind, let's see how the common ways of getting the "last cell" can produce unexpected results:
.End(xlDown)code from the question will break most easily (e.g. with a single non-empty cell or when there are blank cells in between) for the reasons explained in the answer by Siddharth Rout here (search for "xlDown is equally unreliable.") 👎
- Any solution based on
.CurrentRegionwill also break in presence of blank cells or rows 👎
- A solution involving
.End(xlUp)to search backwards from the end of a column will, just as CTRL+UP, look for data (formulas producing a blank value are considered "data") in visible rows (so using it with autofilter enabled might produce incorrect results ⚠️).
You have to take care to avoid the standard pitfalls (for details I'll again refer to the answer by Siddharth Rout here, look for the "Find Last Row in a Column" section), such as hard-coding the last row (
Range("A65536").End(xlUp)) instead of relying on
.SpecialCells(xlLastCell)is equivalent to CTRL+END, returning the bottom-most and right-most cell of the "used range", so all caveats that apply to relying on the "used range", apply to this method as well. In addition, the "used range" is only reset when saving the workbook and when accessing
xlLastCellmight produce stale results⚠️ with unsaved modifications (e.g. after some rows were deleted). See the nearby answer by dotNET.
sht.UsedRange(described in detail in the answer by sancho.s here) considers both data and formatting (though not conditional formatting) and resets the "used range" of the worksheet, which may or may not be what you want.
Note that a common mistake ️is to use
.UsedRange.Rows.Count⚠️, which returns the number of rows in the used range, not the last row number (they will be different if the first few rows are blank), for details see newguy's answer to How can I find last row that contains data in the Excel sheet with a macro?
.Findallows you to find the last row with any data (including formulas) or a non-blank value in any column. You can choose if you're interested in formulas or values, but the catch is that it resets the defaults in the Excel's Find dialog ️️⚠️, which can be highly confusing to your users. It also need to be used carefully, see the answer by Siddharth Rout here (section "Find Last Row in a Sheet")
- More explicit solutions that check individual
Cells' in a loop are generally slower than re-using an Excel function (although can still be performant), but let you specify exactly what you want to find. See my solution based on
UsedRangeand VBA arrays to find the last cell with data in the given column -- it handles hidden rows, filters, blanks, does not modify the Find defaults and is quite performant.
Whatever solution you pick, be careful
- to use
Integerto store the row numbers (to avoid getting
Overflowwith more than 65k rows) and
- to always specify the worksheet you're working with (i.e.
Dim ws As Worksheet ... ws.Range(...)instead of
- when using
.Value(which is a
Variant) avoid implicit casts like
.Value <> ""as they will fail if the cell contains an error value.
Sub lastRow() Dim i As Long i = Cells(Rows.Count, 1).End(xlUp).Row MsgBox i End Sub sub LastRow() 'Paste & for better understanding of the working use F8 Key to run the code . dim WS as worksheet dim i as long set ws = thisworkbook("SheetName") ws.activate ws.range("a1").select ws.range("a1048576").select activecell.end(xlup).select i= activecell.row msgbox "My Last Row Is " & i End sub
For the last 3+ years these are the functions that I am using for finding last row and last column per defined column(for row) and row(for column):
Function lastCol(Optional wsName As String, Optional rowToCheck As Long = 1) As Long Dim ws As Worksheet If wsName = vbNullString Then Set ws = ActiveSheet Else Set ws = Worksheets(wsName) End If lastCol = ws.Cells(rowToCheck, ws.Columns.Count).End(xlToLeft).Column End Function
Function lastRow(Optional wsName As String, Optional columnToCheck As Long = 1) As Long Dim ws As Worksheet If wsName = vbNullString Then Set ws = ActiveSheet Else Set ws = Worksheets(wsName) End If lastRow = ws.Cells(ws.Rows.Count, columnToCheck).End(xlUp).Row End Function
For the case of the OP, this is the way to get the last row in column