How can I find last row that contains data in a specific column?

后端 未结 13 1581
南笙
南笙 2020-11-22 03:44

How can I find the last row that contains data in a specific column and on a specific sheet?

相关标签:
13条回答
  • 2020-11-22 04:06

    You should use the .End(xlup) but instead of using 65536 you might want to use:

    sheetvar.Rows.Count
    

    That way it works for Excel 2007 which I believe has more than 65536 rows

    0 讨论(0)
  • 2020-11-22 04:06
    Last_Row = Range("A1").End(xlDown).Row
    

    Just to verify, let's say you want to print the row number of the last row with the data in cell C1.

    Range("C1").Select
    Last_Row = Range("A1").End(xlDown).Row
    ActiveCell.FormulaR1C1 = Last_Row
    
    0 讨论(0)
  • 2020-11-22 04:09

    All the solutions relying on built-in behaviors (like .Find and .End) have limitations that are not well-documented (see my other answer for details).

    I needed something that:

    • Finds the last non-empty cell (i.e. that has any formula or value, even if it's an empty string) in a specific column
    • Relies on primitives with well-defined behavior
    • Works reliably with autofilters and user modifications
    • Runs as fast as possible on 10,000 rows (to be run in a Worksheet_Change handler without feeling sluggish)
    • ...with performance not falling off a cliff with accidental data or formatting put at the very end of the sheet (at ~1M rows)

    The solution below:

    • Uses UsedRange to find the upper bound for the row number (to make the search for the true "last row" fast in the common case where it's close to the end of the used range);
    • Goes backwards to find the row with data in the given column;
    • ...using VBA arrays to avoid accessing each row individually (in case there are many rows in the UsedRange we need to skip)

    (No tests, sorry)

    ' Returns the 1-based row number of the last row having a non-empty value in the given column (0 if the whole column is empty)
    Private Function getLastNonblankRowInColumn(ws As Worksheet, colNo As Integer) As Long
        ' Force Excel to recalculate the "last cell" (the one you land on after CTRL+END) / "used range"
        ' and get the index of the row containing the "last cell". This is reasonably fast (~1 ms/10000 rows of a used range)
        Dim lastRow As Long: lastRow = ws.UsedRange.Rows(ws.UsedRange.Rows.Count).Row - 1 ' 0-based
    
        ' Since the "last cell" is not necessarily the one we're looking for (it may be in a different column, have some
        ' formatting applied but no value, etc), we loop backward from the last row towards the top of the sheet).
        Dim wholeRng As Range: Set wholeRng = ws.Columns(colNo)
    
        ' Since accessing cells one by one is slower than reading a block of cells into a VBA array and looping through the array,
        ' we process in chunks of increasing size, starting with 1 cell and doubling the size on each iteration, until MAX_CHUNK_SIZE is reached.
        ' In pathological cases where Excel thinks all the ~1M rows are in the used range, this will take around 100ms.
        ' Yet in a normal case where one of the few last rows contains the cell we're looking for, we don't read too many cells.
        Const MAX_CHUNK_SIZE = 2 ^ 10 ' (using large chunks gives no performance advantage, but uses more memory)
        Dim chunkSize As Long: chunkSize = 1
        Dim startOffset As Long: startOffset = lastRow + 1 ' 0-based
        Do ' Loop invariant: startOffset>=0 and all rows after startOffset are blank (i.e. wholeRng.Rows(i+1) for i>=startOffset)
            startOffset = IIf(startOffset - chunkSize >= 0, startOffset - chunkSize, 0)
            ' Fill `vals(1 To chunkSize, 1 To 1)` with column's rows indexed `[startOffset+1 .. startOffset+chunkSize]` (1-based, inclusive)
            Dim chunkRng As Range: Set chunkRng = wholeRng.Resize(chunkSize).Offset(startOffset)
            Dim vals() As Variant
            If chunkSize > 1 Then
                vals = chunkRng.Value2
            Else ' reading a 1-cell range requires special handling <http://www.cpearson.com/excel/ArraysAndRanges.aspx>
                ReDim vals(1 To 1, 1 To 1)
                vals(1, 1) = chunkRng.Value2
            End If
    
            Dim i As Long
            For i = UBound(vals, 1) To LBound(vals, 1) Step -1
                If Not IsEmpty(vals(i, 1)) Then
                    getLastNonblankRowInColumn = startOffset + i
                    Exit Function
                End If
            Next i
    
            If chunkSize < MAX_CHUNK_SIZE Then chunkSize = chunkSize * 2
        Loop While startOffset > 0
    
        getLastNonblankRowInColumn = 0
    End Function
    
    0 讨论(0)
  • 2020-11-22 04:12
    Public Function GetLastRow(ByVal SheetName As String) As Integer
        Dim sht As Worksheet
        Dim FirstUsedRow As Integer     'the first row of UsedRange
        Dim UsedRows As Integer         ' number of rows used
    
        Set sht = Sheets(SheetName)
        ''UsedRange.Rows.Count for the empty sheet is 1
        UsedRows = sht.UsedRange.Rows.Count
        FirstUsedRow = sht.UsedRange.Row
        GetLastRow = FirstUsedRow + UsedRows - 1
    
        Set sht = Nothing
    End Function
    

    sheet.UsedRange.Rows.Count: retrurn number of rows used, not include empty row above the first row used

    if row 1 is empty, and the last used row is 10, UsedRange.Rows.Count will return 9, not 10.

    This function calculate the first row number of UsedRange plus number of UsedRange rows.

    0 讨论(0)
  • 2020-11-22 04:13

    I would like to add one more reliable way using UsedRange to find the last used row:

    lastRow = Sheet1.UsedRange.Row + Sheet1.UsedRange.Rows.Count - 1
    

    Similarly to find the last used column you can see this

    Result in Immediate Window:

    ?Sheet1.UsedRange.Row+Sheet1.UsedRange.Rows.Count-1
     21 
    
    0 讨论(0)
  • 2020-11-22 04:14

    How about:

    Function GetLastRow(strSheet, strColumn) As Long
        Dim MyRange As Range
    
        Set MyRange = Worksheets(strSheet).Range(strColumn & "1")
        GetLastRow = Cells(Rows.Count, MyRange.Column).End(xlUp).Row
    End Function
    

    Regarding a comment, this will return the row number of the last cell even when only a single cell in the last row has data:

    Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
    
    0 讨论(0)
提交回复
热议问题