Lỗi khi tìm ô được sử dụng lần cuối trong Excel với VBA


179

Khi tôi muốn tìm giá trị ô được sử dụng cuối cùng, tôi sử dụng:

Dim LastRow As Long

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

Debug.Print LastRow

Tôi nhận được đầu ra sai khi tôi đặt một phần tử vào một ô. Nhưng khi tôi đặt nhiều hơn một giá trị vào ô, đầu ra là chính xác. Lý do đằng sau điều này là gì?


Câu trả lời:


309

LƯU Ý : Tôi dự định biến đây thành "một điểm dừng" nơi bạn có thể sử dụng Correctcách để tìm hàng cuối cùng. Điều này cũng sẽ bao gồm các thực tiễn tốt nhất để làm theo khi tìm hàng cuối cùng. Và do đó tôi sẽ tiếp tục cập nhật nó bất cứ khi nào tôi gặp một kịch bản / thông tin mới.


Những cách không đáng tin cậy để tìm hàng cuối cùng

Một số cách phổ biến nhất để tìm hàng cuối cùng rất không đáng tin cậy và do đó không bao giờ nên được sử dụng.

  1. Được sử dụng
  2. xlDown
  3. Bá tước

UsedRangecó nên KHÔNG BAO GIỜ được sử dụng để tìm ra ô cuối cùng trong đó có dữ liệu. Nó rất không đáng tin cậy. Hãy thử thí nghiệm này.

Nhập một cái gì đó trong tế bào A5. Bây giờ khi bạn tính hàng cuối cùng với bất kỳ phương thức nào được đưa ra dưới đây, nó sẽ cho bạn 5. Bây giờ tô màu cho ô A10màu đỏ. Nếu bây giờ bạn sử dụng bất kỳ mã nào dưới đây, bạn vẫn sẽ nhận được 5. Nếu bạn sử dụngUsedrange.Rows.Count những gì bạn nhận được? Nó sẽ không phải là 5.

Đây là một kịch bản để hiển thị cách làm UsedRangeviệc.

nhập mô tả hình ảnh ở đây

xlDown là không đáng tin cậy như nhau.

Xem xét mã này

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

Điều gì sẽ xảy ra nếu chỉ có một ô ( A1) có dữ liệu? Bạn sẽ kết thúc đến hàng cuối cùng trong bảng tính! Nó giống như chọn ô A1và sau đó nhấn Endphím và sau đó nhấn Down Arrowphím. Điều này cũng sẽ cung cấp cho bạn kết quả không đáng tin cậy nếu có các ô trống trong một phạm vi.

CountA cũng không đáng tin cậy vì nó sẽ cho bạn kết quả không chính xác nếu có các ô trống ở giữa.

Và do đó ta nên tránh việc sử dụng UsedRange, xlDownCountAđể tìm ô cuối cùng.


Tìm hàng cuối cùng trong một cột

Để tìm hàng cuối cùng trong Col E, hãy sử dụng cái này

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

Nếu bạn nhận thấy rằng chúng tôi có một .trước Rows.Count. Chúng tôi thường chọn bỏ qua điều đó. Xem câu hỏi NÀY về lỗi có thể xảy ra mà bạn có thể nhận được. Tôi luôn khuyên sử dụng .trước Rows.CountColumns.Count. Câu hỏi đó là một kịch bản cổ điển trong đó mã sẽ thất bại vì Rows.Counttrả về 65536cho Excel 2003 trở về trước và 1048576cho Excel 2007 trở lên. Tương tự Columns.Counttrả về 25616384, tương ứng.

Thực tế ở trên là Excel 2007+ có 1048576các hàng cũng nhấn mạnh vào thực tế là chúng ta phải luôn luôn khai báo biến sẽ giữ giá trị hàng Longthay vì Integerkhác, bạn sẽ gặp Overflowlỗi.

Lưu ý rằng phương pháp này sẽ bỏ qua bất kỳ hàng ẩn. Nhìn lại ảnh chụp màn hình của tôi ở trên cho cột A , nếu hàng 8 bị ẩn, cách tiếp cận này sẽ trở lại 5thay vì 8.


Tìm hàng cuối cùng trong một tờ

Để tìm Effectivehàng cuối cùng trong bảng, sử dụng này. Chú ý sử dụng Application.WorksheetFunction.CountA(.Cells). Điều này là bắt buộc vì nếu không có ô nào có dữ liệu trong bảng tính thì .Findsẽ cung cấp cho bạnRun 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

Tìm hàng cuối cùng trong bảng (ListObject)

Các nguyên tắc tương tự được áp dụng, ví dụ để lấy hàng cuối cùng trong cột thứ ba của bảng:

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: Nhập nội dung nào đó vào ô A5. Bây giờ khi bạn tính hàng cuối cùng với bất kỳ phương thức nào được đưa ra ở trên, nó sẽ cho bạn 5. Bây giờ tô màu cho ô A10 màu đỏ. Nếu bây giờ bạn sử dụng bất kỳ mã nào ở trên, bạn vẫn sẽ nhận được 5. Nếu bạn sử dụng Usedrange.Rows.Countnhững gì bạn nhận được? Nó sẽ không phải là 5. Được sử dụng rất không đáng tin cậy để tìm hàng cuối cùng.
Tuyến Siddharth

6
Xin lưu ý rằng. Không may làm rối các cài đặt của người dùng trong hộp thoại Tìm kiếm - tức là Excel chỉ có 1 bộ cài đặt cho hộp thoại và bạn sử dụng. Thay thế chúng. Một mẹo khác là vẫn sử dụng OLX, nhưng sử dụng nó như một mức tối đa tuyệt đối (nhưng không đáng tin cậy) mà từ đó bạn xác định mức tối đa chính xác.
Carl Colijn

4
@CarlColijn: Tôi sẽ không gọi nó là gây rối. :) Excel chỉ đơn giản là rememberscài đặt cuối cùng. Ngay cả khi bạn thực hiện thủ công Find, nó vẫn nhớ cài đặt cuối cùng thực sự là một lợi ích nếu ai đó biết "sự thật" này
Siddharth Rout

3
@KeithPark: Vui lòng tiếp tục :) Kiến thức chỉ có ý nghĩa nếu nó được lan truyền :)
Siddharth Rout

9
Tôi nghĩ rằng mô tả của bạn về UsedRange( rất không đáng tin cậy để tìm ô cuối cùng có dữ liệu ) là sai lệch. UsedRangechỉ đơn giản là không dành cho mục đích đó, mặc dù trong một số trường hợp, nó có thể cho kết quả chính xác. Tôi nghĩ rằng các thí nghiệm đề xuất thêm vào sự nhầm lẫn. Kết quả thu được với UsedRange($ A $ 1: $ A $ 8) không phụ thuộc vào lần nhập dữ liệu đầu tiên khi xóa dữ liệu. Hình bên phải vẫn sẽ giống nhau ngay cả khi không nhập dữ liệu vào dữ liệu đã bị xóa. Xin vui lòng xem câu trả lời của tôi.
sancho.s RebstateMonicaCellio

34

Lưu ý: câu trả lời này được thúc đẩy bởi bình luận này . Mục đích của UsedRangekhác với những gì được đề cập trong câu trả lời ở trên.

Theo cách chính xác để tìm ô được sử dụng cuối cùng, trước tiên người ta phải quyết định xem cái gì được coi là sử dụng , sau đó chọn một phương thức phù hợp . Tôi quan niệm ít nhất ba ý nghĩa:

  1. Được sử dụng = không trống, tức là có dữ liệu .

  2. Được sử dụng = "... đang sử dụng, nghĩa là phần chứa dữ liệu hoặc định dạng ." Theo tài liệu chính thức , đây là tiêu chí được Excel sử dụng tại thời điểm lưu. Xem thêm tài liệu chính thức này . Nếu một người không nhận thức được điều này, tiêu chí có thể tạo ra kết quả không mong muốn, nhưng nó cũng có thể được khai thác có chủ ý (ít thường xuyên hơn, chắc chắn), ví dụ, để làm nổi bật hoặc in các vùng cụ thể, cuối cùng có thể không có dữ liệu. Và, tất nhiên, nó là mong muốn như một tiêu chí cho phạm vi sử dụng khi lưu sổ làm việc, vì sợ mất một phần công việc của một người.

  3. Được sử dụng = "... đang sử dụng, nghĩa là phần chứa dữ liệu hoặc định dạng " hoặc định dạng có điều kiện. Tương tự như 2., nhưng cũng bao gồm các ô là mục tiêu cho bất kỳ quy tắc Định dạng có điều kiện nào.

Làm thế nào để tìm ô được sử dụng cuối cùng phụ thuộc vào những gì bạn muốn (tiêu chí của bạn) .

Đối với tiêu chí 1, tôi đề nghị đọc câu trả lời này . Lưu ý rằng UsedRangeđược trích dẫn là không đáng tin cậy. Tôi nghĩ đó là sai lệch (nghĩa là "không công bằng" UsedRange), vì UsedRangeđơn giản là không có nghĩa là báo cáo ô cuối cùng chứa dữ liệu. Vì vậy, nó không nên được sử dụng trong trường hợp này, như được chỉ ra trong câu trả lời đó. Xem thêm bình luận này .

Đối với tiêu chí 2, UsedRangelà tùy chọn đáng tin cậy nhất , so với các tùy chọn khác cũng được thiết kế cho việc sử dụng này. Nó thậm chí còn không cần thiết phải lưu sổ làm việc để đảm bảo rằng ô cuối cùng được cập nhật. Ctrl+ Endsẽ chuyển đến một ô sai trước khi lưu (Vượt qua ô cuối cùng không được đặt lại cho đến khi bạn lưu bảng tính, từ http://msdn.microsoft.com/en-us/l Library / aa139976% 28v = office.10% 29.aspx . Đây là một tài liệu tham khảo cũ, nhưng về mặt này hợp lệ).

Đối với tiêu chí 3, tôi không biết bất kỳ phương pháp tích hợp nào . Tiêu chí 2 không tính đến Định dạng có điều kiện. Người ta có thể có các ô được định dạng, dựa trên các công thức, không được phát hiện bởi UsedRangehoặc Ctrl+ End. Trong hình, ô cuối cùng là B3, vì định dạng được áp dụng rõ ràng cho nó. Các ô B6: D7 có định dạng xuất phát từ quy tắc Định dạng có điều kiện và điều này không được phát hiện ngay cả bởi UsedRange. Kế toán cho điều này sẽ yêu cầu một số lập trình VBA.

nhập mô tả hình ảnh ở đây


Như câu hỏi cụ thể của bạn : lý do đằng sau này là gì?

Mã của bạn sử dụng ô đầu tiên trong phạm vi E4: E48 của bạn làm tấm bạt lò xo, để nhảy xuống End(xlDown).

Đầu ra "có lỗi" sẽ thu được nếu không có các ô không trống trong phạm vi của bạn ngoài có lẽ là ô đầu tiên. Sau đó, bạn đang nhảy trong bóng tối , tức là xuống bảng tính (bạn nên lưu ý sự khác biệt giữa khoảng trống chuỗichuỗi trống !).

Lưu ý rằng:

  1. Nếu phạm vi của bạn chứa các ô không trống không liền kề, thì nó cũng sẽ cho kết quả sai.

  2. Nếu chỉ có một ô không trống, nhưng nó không phải là ô đầu tiên, mã của bạn vẫn sẽ cho bạn kết quả chính xác.


3
Tôi đồng ý rằng trước tiên người ta phải quyết định những gì được coi là sử dụng . Tôi thấy ít nhất 6 ý nghĩa. Ô có: 1) dữ liệu, nghĩa là một công thức, có thể dẫn đến một giá trị trống; 2) một giá trị, nghĩa là một công thức không trống hoặc hằng số; 3) định dạng; 4) định dạng có điều kiện; 5) một hình dạng (bao gồm Nhận xét) chồng lên ô; 6) sự tham gia vào một Bảng (Danh sách đối tượng). Sự kết hợp nào bạn muốn kiểm tra? Một số (như Bảng) có thể khó kiểm tra hơn và một số có thể hiếm gặp (chẳng hạn như hình dạng bên ngoài phạm vi dữ liệu), nhưng một số khác có thể thay đổi tùy theo tình huống (ví dụ: công thức có giá trị trống).
GlennFromIowa

20

Tôi đã tạo hàm một cửa này để xác định hàng, cột và ô cuối cùng, có thể là dữ liệu, được định dạng (nhóm / nhận xét / ẩn) hoặc định dạng có điều kiện .

Sub LastCellMsg()
    Dim strResult As String
    Dim lngDataRow As Long
    Dim lngDataCol As Long
    Dim strDataCell As String
    Dim strDataFormatRow As String
    Dim lngDataFormatCol As Long
    Dim strDataFormatCell As String
    Dim oFormatCond As FormatCondition
    Dim lngTempRow As Long
    Dim lngTempCol As Long
    Dim lngCFRow As Long
    Dim lngCFCol As Long
    Dim strCFCell As String
    Dim lngOverallRow As Long
    Dim lngOverallCol As Long
    Dim strOverallCell As String

    With ActiveSheet

        If .ListObjects.Count > 0 Then
            MsgBox "Cannot return reliable results, as there is at least one table in the worksheet."
            Exit Sub
        End If

        strResult = "Workbook name: " & .Parent.Name & vbCrLf
        strResult = strResult & "Sheet name: " & .Name & vbCrLf

        'DATA:
        'last data row
        If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
            lngDataRow = .Cells.Find(What:="*", _
             After:=.Range("A1"), _
             Lookat:=xlPart, _
             LookIn:=xlFormulas, _
             SearchOrder:=xlByRows, _
             SearchDirection:=xlPrevious, _
             MatchCase:=False).Row
        Else
            lngDataRow = 1
        End If
        'strResult = strResult & "Last data row: " & lngDataRow & vbCrLf

        'last data column
        If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
            lngDataCol = .Cells.Find(What:="*", _
             After:=.Range("A1"), _
             Lookat:=xlPart, _
             LookIn:=xlFormulas, _
             SearchOrder:=xlByColumns, _
             SearchDirection:=xlPrevious, _
             MatchCase:=False).Column
        Else
            lngDataCol = 1
        End If
        'strResult = strResult & "Last data column: " & lngDataCol & vbCrLf

        'last data cell
        strDataCell = Replace(Cells(lngDataRow, lngDataCol).Address, "$", vbNullString)
        strResult = strResult & "Last data cell: " & strDataCell & vbCrLf

        'FORMATS:
        'last data/formatted/grouped/commented/hidden row
        strDataFormatRow = StrReverse(Split(StrReverse(.UsedRange.Address), "$")(0))
        'strResult = strResult & "Last data/formatted row: " & strDataFormatRow & vbCrLf

        'last data/formatted/grouped/commented/hidden column
        lngDataFormatCol = Range(StrReverse(Split(StrReverse(.UsedRange.Address), "$")(1)) & "1").Column
        'strResult = strResult & "Last data/formatted column: " & lngDataFormatCol & vbCrLf

        'last data/formatted/grouped/commented/hidden cell
        strDataFormatCell = Replace(Cells(strDataFormatRow, lngDataFormatCol).Address, "$", vbNullString)
        strResult = strResult & "Last data/formatted cell: " & strDataFormatCell & vbCrLf

        'CONDITIONAL FORMATS:
        For Each oFormatCond In .Cells.FormatConditions

            'last conditionally-formatted row
            lngTempRow = CLng(StrReverse(Split(StrReverse(oFormatCond.AppliesTo.Address), "$")(0)))
            If lngTempRow > lngCFRow Then lngCFRow = lngTempRow

            'last conditionally-formatted column
            lngTempCol = Range(StrReverse(Split(StrReverse(oFormatCond.AppliesTo.Address), "$")(1)) & "1").Column
            If lngTempCol > lngCFCol Then lngCFCol = lngTempCol
        Next
        'no results are returned for Conditional Format if there is no such
        If lngCFRow <> 0 Then
            'strResult = strResult & "Last cond-formatted row: " & lngCFRow & vbCrLf
            'strResult = strResult & "Last cond-formatted column: " & lngCFCol & vbCrLf

            'last conditionally-formatted cell
            strCFCell = Replace(Cells(lngCFRow, lngCFCol).Address, "$", vbNullString)
            strResult = strResult & "Last cond-formatted cell: " & strCFCell & vbCrLf
        End If

        'OVERALL:
        lngOverallRow = Application.WorksheetFunction.Max(lngDataRow, strDataFormatRow, lngCFRow)
        'strResult = strResult & "Last overall row: " & lngOverallRow & vbCrLf
        lngOverallCol = Application.WorksheetFunction.Max(lngDataCol, lngDataFormatCol, lngCFCol)
        'strResult = strResult & "Last overall column: " & lngOverallCol & vbCrLf
        strOverallCell = Replace(.Cells(lngOverallRow, lngOverallCol).Address, "$", vbNullString)
        strResult = strResult & "Last overall cell: " & strOverallCell & vbCrLf

        MsgBox strResult
        Debug.Print strResult

    End With

End Sub

Kết quả trông như thế này:
xác định ô cuối cùng

Để có kết quả chi tiết hơn, một số dòng trong mã có thể không được ghi chú:
cột cuối cùng, hàng

Một hạn chế tồn tại - nếu có các bảng trong trang tính, kết quả có thể trở nên không đáng tin cậy, vì vậy tôi quyết định tránh chạy mã trong trường hợp này:

If .ListObjects.Count > 0 Then
    MsgBox "Cannot return reliable results, as there is at least one table in the worksheet."
    Exit Sub
End If

2
@franklin - Tôi vừa nhận thấy một tin nhắn hộp thư đến với sự điều chỉnh của bạn đã bị người đánh giá từ chối. Tôi đã sửa chữa sai lầm đó. Tôi đã sử dụng chức năng này một lần khi tôi cần và tôi sẽ sử dụng lại, vì vậy, thực sự, cảm ơn rất nhiều, bạn của tôi!
ZygD

11

Một lưu ý quan trọng cần ghi nhớ khi sử dụng giải pháp ...

LastRow = ws.Cells.Find(What:="*", After:=ws.range("a1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row

... là để đảm bảo rằng LastRowbiến của bạn thuộc Longloại:

Dim LastRow as Long

Nếu không, bạn sẽ nhận được lỗi QUÁ LỚN trong các tình huống nhất định trong sổ làm việc .XLSX

Đây là hàm đóng gói của tôi mà tôi sử dụng cho nhiều mã khác nhau.

Private Function FindLastRow(ws As Worksheet) As Long
    ' --------------------------------------------------------------------------------
    ' Find the last used Row on a Worksheet
    ' --------------------------------------------------------------------------------
    If WorksheetFunction.CountA(ws.Cells) > 0 Then
        ' Search for any entry, by searching backwards by Rows.
        FindLastRow = ws.Cells.Find(What:="*", After:=ws.range("a1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
    End If
End Function

8

Tôi sẽ thêm vào câu trả lời do Siddarth Rout đưa ra để nói rằng cuộc gọi CountA có thể được bỏ qua bằng cách tìm trả về một đối tượng Phạm vi, thay vì số hàng và sau đó kiểm tra đối tượng Phạm vi được trả về để xem đó có phải là Không (bảng tính trống) .

Ngoài ra, tôi sẽ có phiên bản bất kỳ thủ tục LastRow nào của mình trả về 0 cho một bảng tính trống, sau đó tôi có thể biết nó trống.


8

Tôi tự hỏi rằng không ai đã đề cập đến điều này, nhưng cách dễ nhất để có được tế bào được sử dụng cuối cùng là:

Function GetLastCell(sh as Worksheet) As Range
    GetLastCell = sh.Cells(1,1).SpecialCells(xlLastCell)
End Function

Điều này về cơ bản trả về cùng một ô mà bạn nhận được bằng Ctrl+ Endsau khi chọn Ô A1.

Lưu ý: Excel theo dõi ô phía dưới bên phải nhất từng được sử dụng trong bảng tính. Vì vậy, nếu ví dụ bạn nhập một cái gì đó vào B3 và một cái gì đó khác trong H8 và sau đó xóa nội dung của H8 , nhấn Ctrl+ Endvẫn sẽ đưa bạn đến ô H8 . Các chức năng trên sẽ có hành vi tương tự.


2
Last Celltrong Excel đôi khi đề cập đến một ô trống (từ Used Range) khác với Last Used Cell;).
shA.t

1
OP chỉ cần hàng cuối cùng nhưng bạn đã đúng, ô cuối cùng phải là H5 ; Nhưng bạn có thể kiểm tra chức năng của mình sau khi xóa giá trị trong A5 Bạn sẽ thấy ô cuối cùng là ô trống đó và tôi nghĩ rằng mã của bạn cần một số chỉnh sửa như thế Cells(1,1).Select()là không hợp lệ ActiveSheet.Cells(1,1).Select; Ngoài ra, trong VBA, không nên sử dụng Select;).
shA.t

5
Điều này phá vỡ hai quy tắc chính cho Excel VBA: Không sử dụng Chọn! Và đừng cho rằng tờ bạn muốn là bảng đang hoạt động.
Rachel Hettinger

1
Đây là một câu trả lời cũ, nhưng nó thiếu a Set.
BigBen

8

Vì câu hỏi ban đầu là về các vấn đề với việc tìm ô cuối cùng, nên trong câu trả lời này tôi sẽ liệt kê các cách khác nhau để bạn có thể nhận được kết quả bất ngờ ; xem câu trả lời của tôi cho "Làm cách nào tôi có thể tìm thấy hàng cuối cùng chứa dữ liệu trong bảng Excel có macro?" để tôi giải quyết điều này.

Tôi sẽ bắt đầu bằng cách mở rộng câu trả lời của sancho.snhận xét của GlennFromIowa , thêm chi tiết hơn nữa:

[...] Trước tiên, người ta phải quyết định những gì được coi là được sử dụng. Tôi thấy ít nhất 6 ý nghĩa. Tế bào có:

  • 1) dữ liệu, nghĩa là một công thức, có thể dẫn đến một giá trị trống;
  • 2) một giá trị, nghĩa là một công thức không trống hoặc hằng số;
  • 3) định dạng;
  • 4) định dạng có điều kiện;
  • 5) một hình dạng (bao gồm Nhận xét) chồng lên ô;
  • 6) sự tham gia vào một Bảng (Danh sách đối tượng).

Sự kết hợp nào bạn muốn kiểm tra? Một số (như Bảng) có thể khó kiểm tra hơn và một số có thể hiếm gặp (chẳng hạn như hình dạng bên ngoài phạm vi dữ liệu), nhưng một số khác có thể thay đổi tùy theo tình huống (ví dụ: công thức có giá trị trống).

Những thứ khác bạn có thể muốn xem xét:

  • A) Có thể có các hàng ẩn (ví dụ: bộ lọc tự động), các ô trống trống hoặc các hàng trống không?
  • B) Loại hiệu suất nào được chấp nhận?
  • C) Macro VBA có thể ảnh hưởng đến sổ làm việc hoặc cài đặt ứng dụng theo bất kỳ cách nào không?

Với ý nghĩ đó, chúng ta hãy xem làm thế nào các cách phổ biến để có được "tế bào cuối cùng" có thể tạo ra kết quả bất ngờ:

  • Các .End(xlDown)mã từ câu hỏi sẽ phá vỡ một cách dễ dàng nhất (ví dụ với một tế bào không trống đơn hoặc khi có ô trống ở giữa ) vì những lý do giải thích trong các câu trả lời bằng Siddharth rout đây (tìm kiếm cho "xlDown cũng không kém phần đáng tin cậy." ) 👎
  • Bất kỳ giải pháp nào dựa trên Counting ( CountAhoặc Cells*.Count) hoặc .CurrentRegioncũng sẽ phá vỡ khi có các ô hoặc hàng trống
  • Một giải pháp liên quan .End(xlUp)đến tìm kiếm ngược từ cuối cột sẽ, giống như CTRL + UP, tìm kiếm dữ liệu (công thức tạo ra giá trị trống được coi là "dữ liệu") trong các hàng có thể nhìn thấy (do đó, sử dụng tính năng tự động lọc có thể tạo ra kết quả không chính xác ).

    Bạn phải cẩn thận để tránh những cạm bẫy tiêu chuẩn (để biết thêm chi tiết tôi sẽ lại tham khảo câu trả lời của Siddharth Rout tại đây, hãy tìm phần "Tìm hàng cuối cùng trong một cột" ), chẳng hạn như mã hóa cứng hàng cuối cùng ( Range("A65536").End(xlUp)) thay vì dựa vào sht.Rows.Count.

  • .SpecialCells(xlLastCell)tương đương với CTRL + END, trả về ô dưới cùng và ngoài cùng bên phải của "phạm vi đã sử dụng", do đó, tất cả các cảnh báo áp dụng cho "phạm vi đã sử dụng" cũng áp dụng cho phương pháp này. Ngoài ra, "phạm vi đã sử dụng" chỉ được đặt lại khi lưu sổ làm việc và khi truy cập worksheet.UsedRange, do đó xlLastCellcó thể tạo ra kết quả cũ - với các sửa đổi chưa được lưu (ví dụ: sau khi một số hàng bị xóa). Xem câu trả lời gần đó bằng dotNET .
  • sht.UsedRange(được mô tả chi tiết trong câu trả lời của sancho.s tại đây) xem xét cả dữ liệu và định dạng (mặc dù không phải định dạng có điều kiện) và đặt lại "phạm vi đã sử dụng" của bảng tính , có thể hoặc không thể là những gì bạn muốn.

    Lưu ý rằng một lỗi phổ biến ️ là sử dụng .UsedRange.Rows.Count⚠️, trả về số lượng hàng trong phạm vi đã sử dụng, không phải số hàng cuối cùng (chúng sẽ khác nếu một vài hàng đầu tiên trống), để biết chi tiết, hãy xem câu trả lời của newguy về Cách tôi có thể tìm thấy hàng cuối cùng chứa dữ liệu trong bảng Excel có macro?

  • .Findcho phép bạn tìm hàng cuối cùng với bất kỳ dữ liệu nào (bao gồm các công thức) hoặc giá trị không trống trong bất kỳ cột nào . Bạn có thể chọn xem bạn có quan tâm đến công thức hoặc giá trị hay không, nhưng điều thú vị là nó đặt lại mặc định trong hộp thoại Tìm kiếm của Excel , điều này có thể gây nhầm lẫn cho người dùng của bạn. Nó cũng cần được sử dụng cẩn thận, xem câu trả lời của Siddharth Rout tại đây (phần "Tìm hàng cuối cùng trong một tờ" )
  • Các giải pháp rõ ràng hơn kiểm tra từng cá nhân Cellstrong một vòng lặp thường chậm hơn so với sử dụng lại hàm Excel (mặc dù vẫn có thể thực hiện được), nhưng hãy để bạn chỉ định chính xác những gì bạn muốn tìm. Xem giải pháp của tôi dựa trên UsedRangecác mảng và VBA để tìm ô cuối cùng có dữ liệu trong cột đã cho - nó xử lý các hàng ẩn, bộ lọc, khoảng trống, không sửa đổi mặc định Tìm và khá hiệu quả.

Dù bạn chọn giải pháp nào, hãy cẩn thận

  • sử dụng Longthay vì Integerlưu trữ số hàng (để tránh lấy Overflowhơn 65 nghìn hàng) và
  • để luôn chỉ định bảng tính bạn đang làm việc ( Dim ws As Worksheet ... ws.Range(...)thay vì Range(...))
  • khi sử dụng .Value(đó là a Variant), tránh sử dụng các hàm ẩn .Value <> ""như chúng sẽ thất bại nếu ô chứa giá trị lỗi.

4

Tuy nhiên, câu hỏi này đang tìm cách tìm hàng cuối cùng bằng VBA, tôi nghĩ sẽ tốt hơn nếu bao gồm một công thức mảng cho hàm bảng tính vì điều này được truy cập thường xuyên:

{=ADDRESS(MATCH(INDEX(D:D,MAX(IF(D:D<>"",ROW(D:D)-ROW(D1)+1)),1),D:D,0),COLUMN(D:D))}

Bạn cần nhập công thức không có dấu ngoặc rồi nhấn Shift+ Ctrl+Enter để biến nó thành công thức mảng.

Điều này sẽ cung cấp cho bạn địa chỉ của ô được sử dụng cuối cùng trong cột D.


1
Tôi thích điều này. Tôi có thể thay đổi một chút để chỉ nhận được số hàng ... '{= MATCH (INDEX (D: D, MAX (IF (D: D <> "", ROW (D: D) -law (D1) +1)) , 1), D: D, 0)} '
PGSystemTester

3
sub last_filled_cell()
msgbox range("A65536").end(xlup).row
end sub

Đây A65536là ô cuối cùng trong Cột A, mã này đã được thử nghiệm trên excel 2003.


Bạn có thể giải thích làm thế nào mã của bạn trả lời câu hỏi cũ này?
shoover

1
Mặc dù câu trả lời này có thể đúng và hữu ích, nhưng nó được ưu tiên nếu bạn bao gồm một số giải thích cùng với nó để giải thích cách nó giúp giải quyết vấn đề. Điều này trở nên đặc biệt hữu ích trong tương lai, nếu có một sự thay đổi (có thể không liên quan) khiến nó ngừng hoạt động và người dùng cần phải hiểu nó đã hoạt động như thế nào.
Kevin Brown

2

Tôi đang tìm cách bắt chước CTRL+ Shift+ End, vì vậy giải pháp dotNET là tuyệt vời, ngoại trừ với Excel 2010 của tôi, tôi cần thêm một setnếu tôi muốn tránh lỗi:

Function GetLastCell(sh As Worksheet) As Range
  Set GetLastCell = sh.Cells(1, 1).SpecialCells(xlLastCell)
End Function

và làm thế nào để tự kiểm tra điều này:

Sub test()
  Dim ws As Worksheet, r As Range
  Set ws = ActiveWorkbook.Sheets("Sheet1")
  Set r = GetLastCell(ws)
  MsgBox r.Column & "-" & r.Row
End Sub

1

Đây là hai xu của tôi.

IMHO nguy cơ của một hàng ẩn với dữ liệu bị loại trừ là quá quan trọng để xlUpđược coi là câu trả lời Một cửa . Tôi đồng ý rằng nó đơn giản và sẽ hoạt động NHIỀU thời gian, nhưng nó có nguy cơ vượt quá hàng cuối cùng, mà không có bất kỳ cảnh báo nào. Điều này có thể tạo ra thảm họa kết quả tại một số poinit cho ai đó nhảy trên Stack Overlow và đã tìm cách để "cách chắc chắn" để nắm bắt giá trị này.

Các Findphương pháp là hoàn hảo và tôi sẽ chấp nhận nó như là một One Stop trả lời . Tuy nhiên, nhược điểm của việc thay đổi Findcài đặt có thể gây khó chịu, đặc biệt nếu đây là một phần của UDF.

Các câu trả lời khác được đăng là ổn, tuy nhiên sự phức tạp trở nên hơi quá mức. Vì vậy, đây là nỗ lực của tôi để tìm sự cân bằng về độ tin cậy, độ phức tạp tối thiểu và không sử dụng Find.

Function LastRowNumber(Optional rng As Range) As Long

If rng Is Nothing Then
    Set rng = ActiveSheet.UsedRange
Else
    Set rng = Intersect(rng.Parent.UsedRange, rng.EntireColumn)
    If rng Is Nothing Then
        LastRowNumber = 1
        Exit Function
    ElseIf isE = 0 Then
        LastRowNumber = 1
        Exit Function

    End If

End If

LastRowNumber = rng.Cells(rng.Rows.Count, 1).Row

Do While IsEmpty(Intersect(rng, _
    rng.Parent.Rows(LastRowNumber)))

    LastRowNumber = LastRowNumber - 1
Loop

End Function

Tại sao điều này là tốt:

  • Hợp lý đơn giản, không có nhiều biến.
  • Cho phép nhiều cột.
  • Không sửa đổi Findcài đặt
  • Động nếu được sử dụng làm UDF với toàn bộ cột được chọn.

Tại sao điều này là xấu:

  • Với bộ dữ liệu rất lớn và khoảng cách lớn giữa phạm vi được sử dụng và hàng cuối cùng trong các cột được chỉ định, điều này sẽ hoạt động chậm hơn, trong trường hợp hiếm hoi chậm hơn TÍN HIỆU.

Tuy nhiên, tôi nghĩ Giải pháp một cửa có nhược điểm là làm rối findcài đặt hoặc thực hiện chậm hơn là giải pháp tổng thể tốt hơn. Sau đó, người dùng có thể sửa đổi cài đặt của họ để cố gắng cải thiện, biết những gì đang xảy ra với mã của họ. Việc sử dụng xLUpsẽ không cảnh báo về các rủi ro tiềm ẩn và họ có thể tiếp tục cho những người biết bao lâu không biết mã của họ không hoạt động chính xác.


1

Trong hơn 3 năm qua, đây là các hàm mà tôi đang sử dụng để tìm hàng cuối cùng và cột cuối cùng trên mỗi cột được xác định (cho hàng) và hàng (cho cột):

Cột cuối cùng:

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

Hàng cuối cùng:

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

Đối với trường hợp của OP, đây là cách để có được hàng cuối cùng trong cột E:

Debug.Print lastRow(columnToCheck:=Range("E4:E48").Column)

Hàng cuối cùng, đếm hàng trống với dữ liệu:

Ở đây chúng tôi có thể sử dụng các công thức Excel nổi tiếng , cung cấp cho chúng tôi hàng cuối cùng của bảng tính trong Excel, mà không liên quan đến VBA -=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)

Để đưa phần này vào VBA và không ghi bất cứ điều gì vào Excel, sử dụng các tham số cho các hàm sau, một cái gì đó như thế này có thể được ghi nhớ:

Public Function LastRowWithHidden(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

    Dim letters As String
    letters = ColLettersGenerator(columnToCheck)
    LastRowWithHidden = ws.Evaluate("=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(" & letters & "))),ROW(" & letters & " )),0)")

End Function

Function ColLettersGenerator(col As Long) As String

    Dim result As Variant
    result = Split(Cells(1, col).Address(True, False), "$")
    ColLettersGenerator = result(0) & ":" & result(0)

End Function

Điều này sẽ trả về một kết quả không chính xác nếu hàng / cột cuối cùng bị ẩn.
PGSystemTester

@PGSystemTester - có, nhưng theo hiểu biết của tôi, khi tôi lập trình nó, nếu nó bị ẩn thì đó không phải là cột / hàng cuối cùng cần thiết.
Vityata

Vui mừng khi làm việc cho bạn. Tôi nghi ngờ tình huống của bạn không phải là trường hợp sử dụng thông thường. Thường xuyên hơn khi tôi làm việc với các khách hàng cần hàng cuối cùng, họ đang tìm kiếm ô thấp nhất có dữ liệu, không phải ô hiển thị thấp nhất có dữ liệu. Dù sao ... rất vui vì nó hoạt động. 👍
PGSystemTester

@PGSystemTester - Tôi có quan điểm của bạn, nhưng quan tâm đến cấu trúc và không cho phép các tế bào vô hình hoạt động như một bùa mê.
Vityata

@PGSystemTester - vâng, nếu tác vụ có thể cho phép các hàng trống, tôi có thể sẽ sử dụng EVAL()công thức Excel nổi tiếng và. Mặc dù mọi người có thể nghĩ đó Eval()là xấu xa và đây là một câu chuyện thú vị khác để viết ...
Vityata

0
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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.