เกิดข้อผิดพลาดในการค้นหาเซลล์ที่ใช้ล่าสุดใน 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 ถูกซ่อนไว้วิธีนี้จะกลับมาแทน58


ค้นหาแถวสุดท้ายในชีต

หากต้องการค้นหา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 ไม่น่าเชื่อถืออย่างยิ่งที่จะค้นหาแถวสุดท้าย
เส้นทาง Siddharth

6
อย่าลืมว่าให้ค้นหาขออภัยที่ทำให้การตั้งค่าของผู้ใช้ในกล่องโต้ตอบค้นหา - เช่น Excel มีการตั้งค่าสำหรับกล่องโต้ตอบเพียง 1 ชุดและคุณใช้. Find จะแทนที่พวกเขา เคล็ดลับอีกอย่างคือการใช้ UsedRange แต่ให้ใช้เป็นค่าสูงสุด (แต่ไม่น่าเชื่อถือ) ซึ่งคุณกำหนดค่าสูงสุดที่ถูกต้อง
Carl Colijn

4
@CarlColijn: ฉันจะไม่เรียกมันว่ายุ่ง :) Excel เป็นเพียงremembersการตั้งค่าล่าสุด แม้ว่าคุณจะทำFindมันด้วยตนเองมันก็ยังจดจำการตั้งค่าล่าสุดซึ่งในความเป็นจริงแล้วเป็นประโยชน์ถ้าใครรู้ "ความจริง" นี้
Siddharth Rout

3
@KeithPark: โปรดไปข้างหน้า :) ความรู้เพียงมีความหมายถ้ามันแพร่กระจาย :)
Siddharth ปราชัย

9
ฉันคิดว่าคำอธิบายของคุณของUsedRange( มันไม่น่าเชื่อถือสูงที่จะหาเซลล์สุดท้ายที่มีข้อมูล ) ทำให้เข้าใจผิด UsedRangeไม่ได้มีวัตถุประสงค์เพื่อวัตถุประสงค์นั้นแม้ว่าในบางกรณีอาจให้ผลลัพธ์ที่ถูกต้อง ฉันคิดว่าการทดลองที่เสนอเพิ่มความสับสน ผลลัพธ์ที่ได้ด้วยUsedRange($ A $ 1: $ A $ 8) ไม่ได้ขึ้นอยู่กับการป้อนข้อมูลก่อนที่จะลบมัน ตัวเลขทางด้านขวาจะยังคงเหมือนเดิมแม้ว่าจะไม่ได้ลบข้อมูลก็ตาม โปรดดูคำตอบของฉัน
sancho.s ReinstateMonicaCellio

34

หมายเหตุ: คำตอบนี้ได้รับแรงบันดาลใจจากความคิดเห็นนี้ วัตถุประสงค์ของการใช้งานUsedRangeนั้นแตกต่างจากที่กล่าวไว้ในคำตอบข้างต้น

ในฐานะที่เป็นวิธีที่ถูกต้องในการหามือถือล่าสุดหนึ่งมีครั้งแรกในการตัดสินใจสิ่งที่ถือว่าใช้แล้วเลือกวิธีการที่เหมาะสม ฉันเข้าใจความหมายอย่างน้อยสามข้อ:

  1. มือสอง = ไม่ใช่ว่างเปล่าคือมีข้อมูล

  2. Used = "... กำลังใช้งานหมายถึงส่วนที่มีข้อมูลหรือการจัดรูปแบบ " ตามเอกสารอย่างเป็นทางการนี่เป็นเกณฑ์ที่ Excel ใช้ในเวลาที่บันทึก ดูเอกสารประกอบอย่างเป็นทางการนี้ หากไม่มีใครรู้เรื่องนี้เกณฑ์อาจให้ผลลัพธ์ที่ไม่คาดคิด แต่มันอาจถูกนำไปใช้โดยเจตนา (น้อยกว่าแน่นอน) เช่นเพื่อเน้นหรือพิมพ์ภูมิภาคเฉพาะซึ่งในที่สุดอาจไม่มีข้อมูล และแน่นอนว่าเป็นที่พึงปรารถนาว่าเป็นเกณฑ์สำหรับช่วงที่จะใช้เมื่อบันทึกเวิร์กบุ๊กเพื่อมิให้สูญเสียส่วนหนึ่งของงาน

  3. Used = "... กำลังใช้งานหมายถึงส่วนที่มีข้อมูลหรือการจัดรูปแบบ " หรือการจัดรูปแบบตามเงื่อนไข เหมือนกับ 2. แต่รวมเซลล์ที่เป็นเป้าหมายสำหรับกฎการจัดรูปแบบตามเงื่อนไข

วิธีการหามือถือสุดท้ายขึ้นอยู่กับสิ่งที่คุณต้องการ (เกณฑ์ของคุณ)

สำหรับเกณฑ์ที่ 1 ฉันขอแนะนำให้อ่านคำตอบนี้ โปรดทราบว่าUsedRangeอ้างว่าไม่น่าเชื่อถือ ฉันคิดว่านั่นเป็นสิ่งที่ทำให้เข้าใจผิด (เช่น "ไม่ยุติธรรม" ถึงUsedRange) เนื่องจากUsedRangeไม่ได้หมายถึงการรายงานเซลล์สุดท้ายที่มีข้อมูล ดังนั้นจึงไม่ควรใช้ในกรณีนี้ตามที่ระบุไว้ในคำตอบนั้น ดูความคิดเห็นนี้ด้วย

สำหรับเกณฑ์ที่ 2 UsedRangeเป็นตัวเลือกที่น่าเชื่อถือที่สุดเมื่อเทียบกับตัวเลือกอื่น ๆ ที่ออกแบบมาสำหรับการใช้งานนี้ มันยังทำให้ไม่จำเป็นต้องบันทึกเวิร์กบุคเพื่อให้แน่ใจว่าเซลล์ล่าสุดได้รับการอัพเดต Ctrl+ Endจะไปยังเซลล์ที่ไม่ถูกต้องก่อนบันทึก (“ เซลล์สุดท้ายจะไม่ถูกรีเซ็ตจนกว่าคุณจะบันทึกแผ่นงาน” จาก http://msdn.microsoft.com/en-us/library/aa139976%28v=office.10% 29.aspxมันเป็นข้อมูลอ้างอิงเก่า แต่ในส่วนนี้ถูกต้อง)

สำหรับเกณฑ์ที่ 3 ผมไม่ทราบว่าใด ๆ ในตัววิธีการ เกณฑ์ 2 ไม่ได้คำนึงถึงการจัดรูปแบบตามเงื่อนไข หนึ่งอาจจะมีรูปแบบเซลล์ขึ้นอยู่กับสูตรที่ไม่ได้ตรวจพบโดยUsedRangeหรือ+Ctrl Endในรูปเซลล์สุดท้ายคือ B3 เนื่องจากการจัดรูปแบบถูกนำไปใช้กับเซลล์นั้นอย่างชัดเจน เซลล์ B6: D7 UsedRangeมีรูปแบบที่ได้มาจากกฎรูปแบบตามเงื่อนไขและนี้ไม่ถูกตรวจจับได้โดย การบัญชีสำหรับเรื่องนี้จะต้องมีการเขียนโปรแกรม VBA

ป้อนคำอธิบายรูปภาพที่นี่


สำหรับคำถามเฉพาะของคุณ : อะไรคือสาเหตุของสิ่งนี้

รหัสของคุณใช้มือถือเป็นครั้งแรกในช่วงของคุณ E4: E48 เป็นผ้าใบสำหรับการกระโดดEnd(xlDown)ลงไปด้วย

เอาต์พุต "ผิดพลาด" จะได้รับหากไม่มีเซลล์ที่ไม่ว่างในช่วงของคุณนอกเหนือจากเซลล์แรก จากนั้นคุณจะกระโดดในที่มืดเช่นลงแผ่น (คุณควรทราบความแตกต่างระหว่างว่างเปล่าและสตริงที่ว่างเปล่า !)

โปรดทราบว่า:

  1. หากช่วงของคุณมีเซลล์ที่ไม่ต่อเนื่องแบบไม่ต่อเนื่องเซลล์นั้นจะให้ผลลัพธ์ที่ไม่ถูกต้อง

  2. หากมีเซลล์ที่ไม่ว่างเปล่าเพียงเซลล์เดียว แต่ไม่ใช่เซลล์แรกรหัสของคุณจะยังคงให้ผลลัพธ์ที่ถูกต้อง


3
ผมยอมรับว่าหนึ่งมีครั้งแรกในการตัดสินใจสิ่งที่ถือว่าใช้ ฉันเห็นความหมายอย่างน้อย 6 ข้อ เซลล์มี: 1) ข้อมูลเช่นสูตรอาจส่งผลให้เกิดค่าว่าง 2) ค่าคือสูตรที่ไม่ว่างเปล่าหรือค่าคงที่ 3) การจัดรูปแบบ; 4) การจัดรูปแบบตามเงื่อนไข; 5) รูปร่าง (รวมถึงความคิดเห็น) ซ้อนทับเซลล์; 6) การมีส่วนร่วมใน Table (List Object) ชุดค่าผสมใดที่คุณต้องการทดสอบ บางคน (เช่นตาราง) อาจทดสอบได้ยากกว่าและบางรายการอาจหายาก (เช่นรูปร่างนอกช่วงข้อมูล) แต่บางรายการอาจแตกต่างกันไปตามสถานการณ์ (เช่นสูตรที่มีค่าว่าง)
GlennFromIowa

20

ฉันสร้างนี้ฟังก์ชั่นแบบครบวงจรสำหรับการกำหนดแถวสุดท้ายคอลัมน์และเซลล์ไม่ว่าจะเป็นข้อมูลการจัดรูปแบบ (จัดกลุ่ม / ความเห็น / ซ่อนไว้) เซลล์หรือรูปแบบตามเงื่อนไข

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

ผลลัพธ์มีลักษณะเช่นนี้:
กำหนดเซลล์สุดท้าย

สำหรับผลลัพธ์โดยละเอียดเพิ่มเติมบางบรรทัดในโค้ดสามารถไม่ใส่เครื่องหมายข้อคิดเห็น:
คอลัมน์สุดท้ายแถว

มีข้อ จำกัด หนึ่งข้อ - หากมีตารางในแผ่นงานผลลัพธ์อาจไม่น่าเชื่อถือดังนั้นฉันตัดสินใจหลีกเลี่ยงการเรียกใช้รหัสในกรณีนี้:

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 - ฉันเพิ่งสังเกตเห็นข้อความในกล่องจดหมายพร้อมกับการแก้ไขของคุณซึ่งถูกปฏิเสธโดยผู้ตรวจสอบ ฉันแก้ไขข้อผิดพลาดนั้น ฉันใช้ฟังก์ชั่นนี้แล้วเมื่อฉันต้องการและฉันจะใช้อีกครั้งดังนั้นจริงๆขอบคุณมากเพื่อนของฉัน!
ZygD

11

หมายเหตุสำคัญหนึ่งที่ควรทราบเมื่อใช้โซลูชัน ...

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

... เพื่อให้แน่ใจว่าLastRowตัวแปรของคุณเป็นLongประเภท:

Dim LastRow as Long

มิฉะนั้นคุณจะได้รับข้อผิดพลาด OVERFLOW ในบางสถานการณ์ในสมุดงาน. XLSX

นี่คือฟังก์ชั่นที่ห่อหุ้มของฉันที่ฉันไปใช้ในการใช้รหัสต่างๆ

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

ฉันจะเพิ่มคำตอบที่กำหนดโดย Siddarth Rout เพื่อบอกว่าการเรียก CountA สามารถถูกข้ามได้โดยให้ Find return a Range object แทนหมายเลขแถวแล้วทดสอบวัตถุ Range ที่ส่งคืนเพื่อดูว่าไม่มีอะไร (แผ่นงานเปล่า) .

นอกจากนี้ฉันจะให้ LastRow โพรซีเดอร์เวอร์ชันใด ๆ ของฉันคืนค่าศูนย์สำหรับเวิร์กชีตเปล่าแล้วฉันก็รู้ว่ามันว่างเปล่า


8

ฉันสงสัยว่าไม่มีใครพูดถึงเรื่องนี้ แต่วิธีที่ง่ายที่สุดในการรับเซลล์ที่ใช้ล่าสุดคือ:

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

นี้เป็นหลักผลตอบแทนเซลล์เดียวกันที่คุณได้รับจากCtrl+ หลังจากเลือกมือถือEndA1

คำเตือน: Excel ติดตามเซลล์ล่างขวาสุดที่เคยใช้ในแผ่นงาน ดังนั้นถ้าเช่นคุณป้อนบางสิ่งบางอย่างในB3และอย่างอื่นในH8และหลังจากนั้นเมื่อลบเนื้อหาของH8การกดCtrl+ Endจะยังคงนำคุณไปยังเซลล์H8 ฟังก์ชั่นด้านบนจะมีพฤติกรรมเดียวกัน


2
Last Cellใน Excel บางครั้งหมายถึงเซลล์ว่าง (จากUsed Range) ที่แตกต่างจากLast Used Cell;)
shA.t

1
สหกรณ์จำเป็นเพียงแถวสุดท้าย แต่คุณมีสิทธิเซลล์สุดท้ายควรจะH5 ; แต่คุณสามารถทดสอบการทำงานของคุณหลังจากลบค่าในA5คุณจะเห็นว่าเซลล์สุดท้ายคือเซลล์ว่างเปล่าและฉันคิดว่ารหัสของคุณต้องการการแก้ไขบางอย่างเช่นที่Cells(1,1).Select()ไม่ถูกต้องมันอาจจะเป็นActiveSheet.Cells(1,1).Select; นอกจากนี้ใน VBA ไม่แนะนำให้ใช้Select;)
shA.t

5
นี่จะแบ่งกฎที่สำคัญสองข้อสำหรับ Excel VBA: ห้ามใช้เลือก! และอย่าคิดว่าแผ่นงานที่คุณต้องการนั้นเป็นแผ่นงานที่ใช้งานอยู่
Rachel Hettinger

1
นี่คือคำตอบที่เก่า Setแต่ก็เป็นที่ขาดหายไป
BigBen

8

ตั้งแต่คำถามเดิมเป็นเรื่องเกี่ยวกับปัญหาที่เกิดขึ้นกับการหามือถือที่ผ่านมาในคำตอบนี้ฉันจะแสดงวิธีการต่างๆที่คุณจะได้รับผลที่ไม่คาดคิด ; ดูคำตอบของฉันที่"ฉันจะหาแถวสุดท้ายที่มีข้อมูลในแผ่นงาน Excel ด้วยแมโครได้อย่างไร" สำหรับฉันในการแก้ปัญหานี้

ฉันจะเริ่มต้นด้วยการขยายคำตอบโดย sancho.sและความคิดเห็นโดย GlennFromIowaเพิ่มรายละเอียดมากขึ้น:

[... ] มีคนแรกที่ตัดสินใจว่าจะใช้อย่างไร ฉันเห็นความหมายอย่างน้อย 6 ข้อ เซลล์มี:

  • 1) ข้อมูลเช่นสูตรอาจส่งผลให้เกิดค่าว่าง
  • 2) ค่าคือสูตรที่ไม่ว่างเปล่าหรือค่าคงที่
  • 3) การจัดรูปแบบ;
  • 4) การจัดรูปแบบตามเงื่อนไข;
  • 5) รูปร่าง (รวมถึงความคิดเห็น) ซ้อนทับเซลล์;
  • 6) การมีส่วนร่วมใน Table (List Object)

ชุดค่าผสมใดที่คุณต้องการทดสอบ บางคน (เช่นตาราง) อาจทดสอบได้ยากกว่าและบางรายการอาจหายาก (เช่นรูปร่างนอกช่วงข้อมูล) แต่บางรายการอาจแตกต่างกันไปตามสถานการณ์ (เช่นสูตรที่มีค่าว่าง)

สิ่งอื่น ๆ ที่คุณอาจต้องการพิจารณา:

  • A) สามารถมีแถวที่ซ่อนอยู่ (เช่นตัวกรองอัตโนมัติ) เซลล์ว่างหรือแถวว่างได้หรือไม่?
  • B) การแสดงประเภทใดที่ยอมรับได้?
  • C) แมโคร VBA มีผลกับสมุดงานหรือการตั้งค่าแอปพลิเคชันในทางใดทางหนึ่งหรือไม่

โดยที่ในใจเรามาดูกันว่าวิธีทั่วไปในการรับ "เซลล์สุดท้าย" สามารถสร้างผลลัพธ์ที่ไม่คาดคิด:

  • .End(xlDown)โค้ดจากคำถามที่จะทำลายได้ง่ายที่สุด (เช่นกับเซลล์ที่ไม่ว่างเปล่าเดียวหรือเมื่อมีเซลล์ว่างในระหว่าง ) ด้วยเหตุผลที่อธิบายไว้ในคำตอบโดย Siddharth ปราชัยนี่ (ค้นหา"xlDown จะไม่น่าเชื่อถืออย่างเท่าเทียมกัน." ) 👎
  • วิธีการแก้ปัญหาใด ๆ ที่อิงตามCountไอเอ็นจี ( CountAหรือCells*.Count) หรือ.CurrentRegionจะแตกต่อหน้าเซลล์ว่างหรือแถว👎
  • วิธีแก้ปัญหาที่เกี่ยวข้องกับ.End(xlUp)การค้นหาย้อนหลังจากจุดสิ้นสุดของคอลัมน์จะเป็นเช่นเดียวกับ CTRL + UP ค้นหาข้อมูล (สูตรที่สร้างค่าว่างจะถูกพิจารณาว่าเป็น "ข้อมูล") ในแถวที่มองเห็น (ดังนั้นการใช้มันด้วยการเปิดใช้งาน )

    คุณต้องระมัดระวังเพื่อหลีกเลี่ยงข้อผิดพลาดมาตรฐาน (สำหรับรายละเอียดฉันจะอ้างถึงคำตอบของ Siddharth Routอีกครั้งที่นี่ให้ค้นหาส่วน"ค้นหาแถวสุดท้ายในคอลัมน์" ) เช่นการเข้ารหัสแถวที่สุดท้าย ( Range("A65536").End(xlUp)) sht.Rows.Countแทนที่จะอาศัย

  • .SpecialCells(xlLastCell)เทียบเท่ากับ CTRL + END ส่งคืนเซลล์ล่างสุดและขวาสุดของ "ช่วงที่ใช้" ดังนั้นคำเตือนทั้งหมดที่ใช้กับการพึ่งพา "ช่วงที่ใช้" นำไปใช้กับวิธีนี้เช่นกัน นอกจากนี้ "ช่วงที่ใช้" จะถูกรีเซ็ตเมื่อบันทึกเวิร์กบุ๊กและเมื่อเข้าถึงworksheet.UsedRangeเท่านั้นดังนั้นxlLastCellอาจทำให้เกิดผลลัพธ์เก่าที่มีการแก้ไขที่ไม่ได้บันทึก (เช่นหลังจากลบแถวบางแถว) ดูคำตอบที่อยู่บริเวณใกล้เคียงโดย DotNet
  • sht.UsedRange(อธิบายรายละเอียดในคำตอบโดย sancho.sที่นี่) พิจารณาทั้งข้อมูลและการจัดรูปแบบ (แม้ว่าไม่ใช่การจัดรูปแบบตามเงื่อนไข) และรีเซ็ต "ช่วงที่ใช้แล้ว" ของแผ่นงานซึ่งอาจหรืออาจไม่ใช่สิ่งที่คุณต้องการ

    โปรดทราบว่าข้อผิดพลาดทั่วไปคือจะใช้.UsedRange.Rows.Countreturns ซึ่งคืนค่าจำนวนแถวในช่วงที่ใช้ไม่ใช่จำนวนแถวสุดท้าย (จะแตกต่างกันถ้าแถวสองสามแถวแรกว่างเปล่า) สำหรับรายละเอียดดูคำตอบของคนใหม่ของฉัน สุดท้ายที่มีข้อมูลในแผ่นงาน Excel ด้วยมาโคร

  • .Findช่วยให้คุณสามารถที่จะหาแถวสุดท้ายที่มีข้อมูลใด ๆ (รวมทั้งสูตร) หรือค่าที่ไม่ใช่ว่างเปล่าในคอลัมน์ใดคุณสามารถเลือกได้ว่าคุณสนใจสูตรหรือค่าใด ๆ แต่การตรวจจับนั้นเป็นการรีเซ็ตค่าเริ่มต้นในกล่องโต้ตอบค้นหาของ Excel ️️⚠️ซึ่งอาจสร้างความสับสนให้ผู้ใช้ของคุณ นอกจากนี้ยังต้องใช้อย่างระมัดระวังดูคำตอบโดย Siddharth Routที่นี่ (ส่วน"ค้นหาแถวสุดท้ายในแผ่นงาน" )
  • โซลูชันที่ชัดเจนมากขึ้นที่ตรวจสอบแต่ละรายการCells'ในลูปโดยทั่วไปจะช้ากว่าการใช้ฟังก์ชัน Excel อีกครั้ง (แม้ว่าจะยังสามารถใช้งานได้) แต่ให้คุณระบุสิ่งที่คุณต้องการได้อย่างแน่นอน ดูโซลูชันของฉันโดยยึดตามUsedRangeและ VBA เพื่อค้นหาเซลล์สุดท้ายที่มีข้อมูลในคอลัมน์ที่กำหนด - จัดการกับแถวที่ซ่อนอยู่ตัวกรองช่องว่างไม่แก้ไขค่าเริ่มต้นค้นหาและค่อนข้างมีประสิทธิภาพ

ไม่ว่าคุณจะเลือกวิธีใดให้ระวังตัวด้วย

  • เพื่อใช้LongแทนIntegerการเก็บหมายเลขแถว (เพื่อหลีกเลี่ยงการรับOverflowมากกว่า 65k แถว) และ
  • เพื่อระบุแผ่นงานที่คุณทำงานอยู่เสมอ (เช่นDim ws As Worksheet ... ws.Range(...)แทนRange(...))
  • เมื่อใช้.Value(ซึ่งเป็นVariant) หลีกเลี่ยงการปลดเปลื้องโดยนัยเช่น.Value <> ""พวกเขาจะล้มเหลวหากเซลล์มีค่าความผิดพลาด

4

อย่างไรก็ตามคำถามนี้กำลังมองหาแถวสุดท้ายที่ใช้ VBA ฉันคิดว่ามันจะเป็นการดีที่จะรวมสูตรอาร์เรย์สำหรับฟังก์ชั่นแผ่นงานเนื่องจากมีการเข้าชมบ่อยๆ:

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

คุณจำเป็นต้องใส่สูตรโดยไม่ต้องวงเล็บและจากนั้นกดShift+ Ctrl+ Enterที่จะทำให้มันเป็นสูตรอาร์เรย์

สิ่งนี้จะให้ที่อยู่ของเซลล์ที่ใช้ล่าสุดในคอลัมน์ D


1
ฉันชอบสิ่งนี้. ฉันอาจเปลี่ยนแปลงเล็กน้อยเพื่อให้ได้รับหมายเลขแถวเท่านั้น ... '{= การจับคู่ (INDEX (D: D, MAX (ถ้า (D: D <> "", ROW (D: D) -ROW (D1) +1)) , 1), D: D, 0)} '
PGSystemTester

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

นี่A65536คือเซลล์สุดท้ายในคอลัมน์ A รหัสนี้ทดสอบบน excel 2003


คุณช่วยอธิบายว่ารหัสของคุณตอบคำถามเก่านี้ได้อย่างไร
shoover

1
แม้ว่าคำตอบนี้อาจจะถูกต้องและมีประโยชน์ แต่ก็เป็นที่ต้องการถ้าคุณมีคำอธิบายมาด้วยเพื่ออธิบายว่ามันช่วยแก้ปัญหาได้อย่างไร สิ่งนี้จะเป็นประโยชน์อย่างยิ่งในอนาคตหากมีการเปลี่ยนแปลง (อาจไม่เกี่ยวข้อง) ที่ทำให้หยุดทำงานและผู้ใช้จำเป็นต้องเข้าใจว่ามันทำงานอย่างไร
Kevin Brown

2

ฉันกำลังมองหาวิธีที่จะเลียนแบบเครื่องหมายCTRL+ Shift+ Endดังนั้นโซลูชัน dotNET นั้นยอดเยี่ยมยกเว้น Excel 2010 ของฉันฉันต้องเพิ่มsetถ้าฉันต้องการหลีกเลี่ยงข้อผิดพลาด:

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

และวิธีตรวจสอบด้วยตัวคุณเอง:

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

นี่คือสองเซ็นต์ของฉัน

IMHO ความเสี่ยงของแถวที่ซ่อนอยู่กับข้อมูลที่ถูกแยกออกเป็นสำคัญเกินกว่าที่จะปล่อยให้xlUpได้รับการพิจารณาคำตอบหนึ่งหยุด ฉันยอมรับว่ามันง่ายและจะทำงานได้ตลอดเวลา แต่มันแสดงความเสี่ยงในการทำความเข้าใจแถวสุดท้ายโดยไม่มีการเตือนล่วงหน้า สิ่งนี้สามารถสร้างผลลัพธ์CATASTROPHICที่ poinit สำหรับใครบางคนที่กระโดดข้าม Stack Overlow และมองหา "วิธีที่แน่นอน" เพื่อจับภาพค่านี้

Findเป็นวิธีการที่สมบูรณ์แบบและฉันจะเห็นว่ามันเป็นคำตอบ One Stop อย่างไรก็ตามข้อเสียเปรียบของการเปลี่ยนการFindตั้งค่าอาจสร้างความรำคาญโดยเฉพาะอย่างยิ่งหากนี่เป็นส่วนหนึ่งของ UDF

คำตอบอื่น ๆ ที่โพสต์นั้นไม่เป็นไร แต่ความซับซ้อนจะมากเกินไปเล็กน้อย 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

ทำไมสิ่งนี้ถึงดี:

  • เรียบง่ายพอสมควรและมีตัวแปรไม่มาก
  • ช่วยให้หลายคอลัมน์
  • ไม่แก้ไขFindการตั้งค่า
  • ไดนามิกหากใช้เป็น UDF โดยเลือกทั้งคอลัมน์

ทำไมสิ่งนี้ไม่ดี:

  • ด้วยชุดข้อมูลที่มีขนาดใหญ่มากและช่องว่างขนาดใหญ่ระหว่างช่วงที่ใช้และแถวสุดท้ายในคอลัมน์ที่ระบุสิ่งนี้จะทำงานช้าลงในกรณีที่ไม่ค่อยเกิดขึ้นช้าลงอย่างมีนัยสำคัญ

อย่างไรก็ตามฉันคิดว่า One-Stop-Solution ที่มีข้อเสียเปรียบของfindการตั้งค่าหรือการทำงานช้าลงเป็นโซลูชันที่ดีกว่าโดยรวม ผู้ใช้สามารถแก้ไขการตั้งค่าของพวกเขาเพื่อพยายามปรับปรุงโดยรู้ว่าเกิดอะไรขึ้นกับรหัสของพวกเขา การใช้xLUpจะไม่เตือนความเสี่ยงที่อาจเกิดขึ้นและพวกเขาสามารถดำเนินการต่อไปสำหรับผู้ที่รู้ว่านานแค่ไหนที่รู้ว่ารหัสของพวกเขาทำงานไม่ถูกต้อง


1

ในช่วง 3 ปีที่ผ่านมาสิ่งเหล่านี้เป็นฟังก์ชั่นที่ฉันใช้ในการค้นหาแถวสุดท้ายและคอลัมน์สุดท้ายต่อคอลัมน์ที่กำหนด (สำหรับแถว) และแถว (สำหรับคอลัมน์):

คอลัมน์สุดท้าย:

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

สำหรับกรณีของ OP นี่คือวิธีรับแถวสุดท้ายในคอลัมน์E:

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

แถวสุดท้ายนับแถวที่ว่างเปล่าด้วยข้อมูล:

ที่นี่เราอาจใช้สูตร Excel ที่รู้จักกันดีซึ่งให้แถวสุดท้ายของแผ่นงานใน Excel โดยไม่เกี่ยวข้องกับ VBA -=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)

เพื่อที่จะใส่สิ่งนี้ใน VBA และไม่ต้องเขียนอะไรใน Excel โดยใช้พารามิเตอร์สำหรับฟังก์ชั่นหลังสิ่งที่ควรคำนึงถึง:

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

นี่จะส่งคืนผลลัพธ์ที่ไม่ถูกต้องหากซ่อนแถว / คอลัมน์สุดท้าย
PGSystemTester

@PGSystemTester - ใช่ แต่ในความเข้าใจของฉันเมื่อฉันโปรแกรมมันถ้ามันถูกซ่อนไว้มันไม่ได้เป็นคอลัมน์ / แถวสุดท้ายที่จำเป็น
Vityata

ดีใจที่เหมาะกับคุณ ฉันสงสัยว่าสถานการณ์ของคุณไม่ใช่กรณีการใช้งานทั่วไป บ่อยครั้งมากขึ้นเมื่อฉันทำงานกับลูกค้าที่ต้องการแถวสุดท้ายที่พวกเขาจะ seaching สำหรับเซลล์ต่ำสุดที่มีข้อมูลไม่ต่ำสุดที่มองเห็นมือถือที่มีข้อมูล อย่างไรก็ตาม ... ดีใจที่มันได้ผล 👍
PGSystemTester

@PGSystemTester - ฉันได้รับจุดของคุณ แต่การดูแลโครงสร้างและไม่อนุญาตให้เซลล์ที่มองไม่เห็นทำงานได้อย่างมีเสน่ห์
Vityata

@PGSystemTester - ใช่ถ้างานอาจอนุญาตให้แถวว่างเปล่าฉันอาจจะใช้EVAL()และสูตร Excel ที่มีชื่อเสียง แม้ว่าคนอาจจะคิดว่าEval()เป็นสิ่งที่ชั่วร้ายและนี่เป็นอีกเรื่องราวที่น่าสนใจที่จะเขียน ...
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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.