Errore nel trovare l'ultima cella utilizzata in Excel con VBA


179

Quando voglio trovare l'ultimo valore di cella utilizzato, utilizzo:

Dim LastRow As Long

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

Debug.Print LastRow

Sto ottenendo un output errato quando inserisco un singolo elemento in una cella. Ma quando inserisco più di un valore nella cella, l'output è corretto. Qual è la ragione dietro questo?


Risposte:


309

NOTA : ho intenzione di rendere questo un "posto unico" in cui è possibile utilizzare il Correctmodo per trovare l'ultima riga. Questo coprirà anche le migliori pratiche da seguire quando si trova l'ultima riga. E quindi continuerò ad aggiornarlo ogni volta che trovo un nuovo scenario / informazione.


Modi inaffidabili per trovare l'ultima riga

Alcuni dei modi più comuni per trovare l'ultima riga che sono altamente inaffidabili e quindi non dovrebbero mai essere usati.

  1. UsedRange
  2. xlDown
  3. COUNTA

UsedRangenon dovrebbe MAI essere usato per trovare l'ultima cella che contiene dati. È altamente inaffidabile. Prova questo esperimento.

Digita qualcosa nella cella A5. Ora quando calcoli l'ultima riga con uno dei metodi indicati di seguito, ti darà 5. Adesso colora la cella in A10rosso. Se ora usi uno dei seguenti codici, otterrai comunque 5. Se usi Usedrange.Rows.Countcosa ottieni? Non sarà 5.

Ecco uno scenario per mostrare come UsedRangefunziona.

inserisci qui la descrizione dell'immagine

xlDown è altrettanto inaffidabile.

Considera questo codice

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

Cosa succederebbe se ci fosse una sola cella ( A1) con dati? Finirai per raggiungere l'ultima riga del foglio di lavoro! È come selezionare la cella A1e quindi premere il Endtasto e quindi premere il Down Arrowtasto. Questo ti darà anche risultati inaffidabili se ci sono celle vuote in un intervallo.

CountA è anche inaffidabile perché ti darà risultati errati se ci sono celle vuote in mezzo.

E quindi si dovrebbe evitare l'uso di UsedRange, xlDowne CountAper trovare l'ultima cella.


Trova l'ultima riga in una colonna

Per trovare l'ultima riga in Col E usa questo

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

Se noti che abbiamo un .prima Rows.Count. Spesso abbiamo scelto di ignorarlo. Vedi QUESTA domanda sul possibile errore che potresti ricevere. Consiglio sempre di utilizzare .prima Rows.Counte Columns.Count. Questa domanda è uno scenario classico in cui il codice fallirà perché Rows.Countrestituisce 65536Excel 2003 e versioni precedenti e 1048576Excel 2007 e versioni successive. Allo stesso modo Columns.Countrestituisce 256e 16384, rispettivamente.

Il fatto sopra che Excel 2007+ ha 1048576righe sottolinea anche il fatto che dovremmo sempre dichiarare la variabile che conterrà il valore della riga poiché Longinvece di Integerottenere un Overflowerrore.

Si noti che questo approccio salterà tutte le righe nascoste. Guardando indietro al mio screenshot qui sopra per la colonna A , se la riga 8 fosse nascosta, questo approccio ritornerebbe 5invece di 8.


Trova l'ultima riga in un foglio

Per trovare l' Effectiveultima riga nel foglio, usa questo. Si noti l'uso di Application.WorksheetFunction.CountA(.Cells). Questo è necessario perché se non ci sono celle con dati nel foglio di lavoro, allora .Findti darà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

Trova l'ultima riga in una tabella (ListObject)

Si applicano gli stessi principi, ad esempio per ottenere l'ultima riga nella terza colonna di una tabella:

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: Digita qualcosa nella cella A5. Ora, quando calcoli l'ultima riga con uno dei metodi indicati sopra, ti darà 5. Adesso colora la cella A10 di rosso. Se ora usi uno dei codici sopra indicati, otterrai comunque 5. Se usi Usedrange.Rows.Countcosa ottieni? Non sarà 5. Usedrange è altamente inaffidabile per trovare l'ultima riga.
Siddharth Rout,

6
Si noti che .Find sfortunatamente confonde le impostazioni dell'utente nella finestra di dialogo Trova, ovvero Excel ha solo 1 set di impostazioni per la finestra di dialogo e l'utilizzo di. Un altro trucco è ancora utilizzare UsedRange, ma usarlo come massimo assoluto (ma inaffidabile) dal quale si determina il massimo corretto.
Carl Colijn,

4
@CarlColijn: non lo definirei disordinato. :) Excel è semplicemente remembersl'ultima impostazione. Anche quando fai manualmente un Find, si ricorda l'ultima impostazione che in realtà è un vantaggio se si conosce questo "fatto"
Siddharth Rout

3
@KeithPark: Per favore, vai avanti :) La conoscenza ha un significato solo se viene diffusa :)
Siddharth Rout,

9
Penso che la tua descrizione di UsedRange( è altamente inaffidabile trovare l'ultima cella che contiene dati ) sia fuorviante. UsedRangesemplicemente non è destinato a tale scopo, anche se in alcuni casi può dare il risultato corretto. Penso che l'esperimento proposto aumenti la confusione. Il risultato ottenuto con UsedRange($ A $ 1: $ A $ 8) non dipende dalla prima immissione dei dati e dalla loro eliminazione. La figura a destra sarà sempre la stessa anche senza aver inserito i dati e cancellata. Si prega di vedere la mia risposta.
sancho.s ReinstateMonicaCellio il

34

Nota: questa risposta è stata motivata da questo commento . Lo scopo di UsedRangeè diverso da quanto menzionato nella risposta sopra.

Per quanto riguarda il modo corretto di trovare l'ultima cella utilizzata, si deve prima decidere cosa viene considerato utilizzato , quindi selezionare un metodo adatto . Concepisco almeno tre significati:

  1. Usato = non vuoto, ovvero con dati .

  2. Usato = "... in uso, ovvero la sezione che contiene dati o formattazione ." Come da documentazione ufficiale , questo è il criterio utilizzato da Excel al momento del salvataggio. Vedi anche questa documentazione ufficiale . Se non si è a conoscenza di ciò, il criterio può produrre risultati imprevisti, ma può anche essere sfruttato intenzionalmente (meno spesso, sicuramente), ad esempio, per evidenziare o stampare regioni specifiche, che alla fine potrebbero non avere dati. E, naturalmente, è desiderabile come criterio per la gamma utilizzare quando si salva una cartella di lavoro, per non perdere parte del proprio lavoro.

  3. Usato = "... in uso, ovvero la sezione che contiene dati o formattazione " o formattazione condizionale. Come per 2., ma includendo anche le celle che sono la destinazione di qualsiasi regola di formattazione condizionale.

Come trovare l'ultima cella utilizzata dipende da ciò che desideri (il tuo criterio) .

Per il criterio 1, suggerisco di leggere questa risposta . Si noti che UsedRangeè citato come inaffidabile. Penso che sia fuorviante (cioè "ingiusto" UsedRange), in quanto UsedRangesemplicemente non è destinato a riportare l'ultima cella contenente dati. Quindi non dovrebbe essere usato in questo caso, come indicato in quella risposta. Vedi anche questo commento .

Per il criterio 2, UsedRangeè l'opzione più affidabile , rispetto ad altre opzioni progettate anche per questo uso. Inoltre, non è necessario salvare una cartella di lavoro per assicurarsi che l'ultima cella sia aggiornata. Ctrl+ Endandrà in una cella errata prima del salvataggio ("L'ultima cella non viene ripristinata fino a quando non si salva il foglio di lavoro", da http://msdn.microsoft.com/en-us/library/aa139976%28v=office.10% 29.aspx È un vecchio riferimento, ma a questo proposito valido).

Per il criterio 3, non conosco alcun metodo integrato . Il criterio 2 non tiene conto della formattazione condizionale. Uno può avere celle formattate, basate su formule, che non vengono rilevate da UsedRangeo Ctrl+ End. Nella figura, l'ultima cella è B3, poiché la formattazione è stata applicata esplicitamente ad essa. Le celle B6: D7 hanno un formato derivato da una regola di formattazione condizionale e questo non viene rilevato nemmeno da UsedRange. La contabilità per questo richiederebbe una certa programmazione VBA.

inserisci qui la descrizione dell'immagine


Per quanto riguarda la tua domanda specifica : qual è il motivo dietro questo?

Il tuo codice utilizza la prima cella nel tuo intervallo E4: E48 come trampolino, per saltare con End(xlDown).

L'output "errato" si otterrà se non ci sono celle non vuote nel tuo intervallo oltre forse la prima. Quindi, si sta saltando nel buio , vale a dire, in fondo al foglio di lavoro (si dovrebbe notare la differenza tra il vuoto e stringa vuota !).

Nota che:

  1. Se il tuo intervallo contiene celle non vuote non contigue, fornirà anche un risultato errato.

  2. Se esiste una sola cella non vuota, ma non è la prima, il tuo codice ti darà comunque il risultato corretto.


3
Concordo sul fatto che uno deve prima decidere cosa viene considerato usato . Vedo almeno 6 significati. La cella ha: 1) dati, ovvero una formula, che può comportare un valore vuoto; 2) un valore, ovvero una formula o una costante non vuota; 3) formattazione; 4) formattazione condizionale; 5) una forma (incluso Comment) sovrapposta alla cella; 6) coinvolgimento in una tabella (oggetto elenco). Per quale combinazione vuoi provare? Alcuni (come le tabelle) possono essere più difficili da testare e alcuni possono essere rari (come una forma al di fuori dell'intervallo di dati), ma altri possono variare in base alla situazione (ad esempio, formule con valori vuoti).
GlennFromIowa,

20

Ho creato questa funzione one-stop per determinare l'ultima riga, colonna e cella, che si tratti di dati, celle formattate (raggruppate / commentate / nascoste) o formattazione condizionale .

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

I risultati si presentano così:
determinare l'ultima cella

Per risultati più dettagliati, alcune righe del codice possono essere decommentate:
ultima colonna, riga

Esiste una limitazione: se ci sono tabelle nel foglio, i risultati possono diventare inaffidabili, quindi ho deciso di evitare di eseguire il codice in questo caso:

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 - Ho appena notato un messaggio di posta in arrivo con la tua correzione che è stata respinta dai revisori. Ho corretto quell'errore. Ho già usato questa funzione una volta quando ne avevo bisogno e la userò di nuovo, quindi grazie mille, amico mio!
ZygD,

11

Una nota importante da tenere a mente quando si utilizza la soluzione ...

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

... è per garantire che la tua LastRowvariabile sia di Longtipo:

Dim LastRow as Long

In caso contrario, si otterranno errori OVERFLOW in determinate situazioni nelle cartelle di lavoro .XLSX

Questa è la mia funzione incapsulata che passo a vari usi del codice.

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

Aggiungerei la risposta data da Siddarth Rout per dire che la chiamata CountA può essere saltata avendo Trova restituisce un oggetto Range, anziché un numero di riga, e quindi testa l'oggetto Range restituito per vedere se è Nothing (foglio di lavoro vuoto) .

Inoltre, vorrei che la mia versione di qualsiasi procedura LastRow restituisse uno zero per un foglio di lavoro vuoto, quindi posso sapere che è vuoto.


8

Mi chiedo che nessuno lo abbia menzionato, ma il modo più semplice per ottenere l'ultima cella utilizzata è:

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

Questo essenzialmente restituisce la stessa cella che ottieni per Ctrl+ Enddopo aver selezionato Cella A1.

Un avvertimento: Excel tiene traccia della cella più in basso a destra mai usata in un foglio di lavoro. Quindi, se ad esempio inserisci qualcosa in B3 e qualcos'altro in H8 e successivamente elimini il contenuto di H8 , premendo Ctrl+ Endti porterà ancora nella cella H8 . La funzione sopra avrà lo stesso comportamento.


2
Last Cellin Excel a volte si riferisce a una cella vuota (da Used Range) diversa da Last Used Cell;).
shA.t

1
L'OP aveva bisogno solo dell'ultima riga ma hai ragione, l'ultima cella dovrebbe essere H5 ; Ma puoi testare la tua funzione dopo aver eliminato il valore in A5 Vedrai che l'ultima cella è quella cella vuota, e penso che il tuo codice abbia bisogno di alcune modifiche come questa Cells(1,1).Select()non è valido, forse lo è ActiveSheet.Cells(1,1).Select; Anche in VBA non è raccomandato l'uso Select;).
shA.t

5
Ciò infrange due regole cardinali per Excel VBA: non utilizzare Seleziona! E non dare per scontato che il foglio desiderato sia quello attivo.
Rachel Hettinger il

1
Questa è una vecchia risposta, ma manca un Set.
BigBen,

8

Poiché la domanda originale riguarda i problemi con la ricerca dell'ultima cella, in questa risposta elencherò i vari modi in cui è possibile ottenere risultati imprevisti ; vedere la mia risposta a "Come posso trovare l'ultima riga che contiene i dati nel foglio Excel con una macro?" per la mia opinione su come risolvere questo.

Inizierò espandendo la risposta di sancho.s e il commento di GlennFromIowa , aggiungendo ancora più dettagli:

[...] si deve prima decidere cosa viene considerato usato. Vedo almeno 6 significati. La cella ha:

  • 1) dati, ovvero una formula, che può comportare un valore vuoto;
  • 2) un valore, ovvero una formula o una costante non vuota;
  • 3) formattazione;
  • 4) formattazione condizionale;
  • 5) una forma (incluso Comment) sovrapposta alla cella;
  • 6) coinvolgimento in una tabella (oggetto elenco).

Per quale combinazione vuoi provare? Alcuni (come le tabelle) possono essere più difficili da testare e alcuni possono essere rari (come una forma al di fuori dell'intervallo di dati), ma altri possono variare in base alla situazione (ad esempio, formule con valori vuoti).

Altre cose che potresti voler considerare:

  • A) Possono esserci righe nascoste (ad es. Filtro automatico), celle vuote o righe vuote?
  • B) Che tipo di prestazione è accettabile?
  • C) La macro VBA può influire in qualche modo sulla cartella di lavoro o sulle impostazioni dell'applicazione?

Con questo in mente, vediamo come i modi comuni per ottenere "l'ultima cella" possono produrre risultati inaspettati:

  • Il .End(xlDown)codice della domanda si interromperà più facilmente (ad esempio con una singola cella non vuota o quando ci sono celle vuote in mezzo ) per i motivi spiegati nella risposta di Siddharth Rout qui (la ricerca di "xlDown è ugualmente inaffidabile." ) 👎
  • Qualsiasi soluzione basata su Counting ( CountAo Cells*.Count) o .CurrentRegionsi romperà anche in presenza di celle o righe vuote 👎
  • Una soluzione che prevede la .End(xlUp)ricerca all'indietro dalla fine di una colonna cercherà, proprio come CTRL + SU, i dati (le formule che producono un valore vuoto sono considerate "dati") in righe visibili (quindi usarlo con il filtro automatico abilitato potrebbe produrre risultati errati ⚠️ ).

    Devi fare attenzione ad evitare le insidie ​​standard (per i dettagli farò di nuovo riferimento alla risposta di Siddharth Rout qui, cerca la sezione "Trova l'ultima riga in una colonna" ), come codificare a fondo l'ultima riga ( Range("A65536").End(xlUp)) invece di fare affidamento sht.Rows.Count.

  • .SpecialCells(xlLastCell)equivale a CTRL + END, che restituisce la cella più in basso e quella più a destra dell '"intervallo utilizzato", quindi tutti gli avvertimenti che si applicano basandosi sull'intervallo "usato", si applicano anche a questo metodo. Inoltre, l '"intervallo utilizzato" viene ripristinato solo durante il salvataggio della cartella di lavoro e durante l'accesso worksheet.UsedRange, pertanto xlLastCellpotrebbe produrre risultati non aggiornati⚠️ con modifiche non salvate (ad esempio dopo l'eliminazione di alcune righe). Vedi la risposta vicina di dotNET .
  • sht.UsedRange(descritto in dettaglio nella risposta di sancho.s qui) considera sia i dati che la formattazione (anche se non la formattazione condizionale) e reimposta la "gamma utilizzata" del foglio di lavoro , che può essere o meno ciò che desideri.

    Si noti che un errore comune ️ è usare .UsedRange.Rows.Count⚠️, che restituisce il numero di righe nell'intervallo utilizzato, non l' ultimo numero di riga (saranno diversi se le prime righe sono vuote), per i dettagli vedere la risposta di Newguy a Come posso trovare ultima riga che contiene i dati nel foglio Excel con una macro?

  • .Findti consente di trovare l'ultima riga con qualsiasi dato (comprese le formule) o un valore non vuoto in qualsiasi colonna . Puoi scegliere se sei interessato a formule o valori, ma il problema è che ripristina i valori predefiniti nella finestra di dialogo Trova di Excel ️️⚠️, che può essere molto fonte di confusione per i tuoi utenti. Deve anche essere usato con attenzione, vedere la risposta di Siddharth Rout qui (sezione "Trova l'ultima riga in un foglio" )
  • Le soluzioni più esplicite che controllano l'individuo Cellsin un ciclo sono generalmente più lente rispetto al riutilizzo di una funzione Excel (sebbene possa essere comunque performante), ma consentono di specificare esattamente ciò che si desidera trovare. Vedi la mia soluzione basata su UsedRangee array VBA per trovare l'ultima cella con i dati nella colonna data: gestisce righe nascoste, filtri, spazi vuoti, non modifica le impostazioni predefinite Trova ed è abbastanza performante.

Qualunque sia la soluzione che scegli, fai attenzione

  • da utilizzare Longinvece di Integermemorizzare i numeri di riga (per evitare di ottenere Overflowcon più di 65.000 righe) e
  • per specificare sempre il foglio di lavoro con cui stai lavorando (ovvero Dim ws As Worksheet ... ws.Range(...)invece di Range(...))
  • quando si utilizza .Value(che è a Variant) evitare cast impliciti .Value <> ""come se falliranno se la cella contiene un valore di errore.

4

Tuttavia, questa domanda sta cercando di trovare l'ultima riga utilizzando VBA, penso che sarebbe utile includere una formula di matrice per la funzione del foglio di lavoro poiché questa viene visitata frequentemente:

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

Devi inserire la formula senza parentesi e quindi premere Shift+ Ctrl+ Enterper renderla una formula di matrice.

Questo ti darà l'indirizzo dell'ultima cella utilizzata nella colonna D.


1
Mi piace questo. Potrei modificare leggermente per ottenere solo il numero di riga ... '{= MATCH (INDICE (D: D, MAX (IF (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

Ecco A65536l'ultima cella della colonna A, questo codice è stato testato su Excel 2003.


Puoi spiegare come il tuo codice risponde a questa vecchia domanda?
sostituzione

1
Sebbene questa risposta sia probabilmente corretta e utile, è preferibile includere una spiegazione insieme per spiegare come aiuta a risolvere il problema. Ciò diventa particolarmente utile in futuro, se c'è un cambiamento (possibilmente non correlato) che lo fa smettere di funzionare e gli utenti devono capire come funzionava una volta.
Kevin Brown,

2

Stavo cercando un modo per imitare il CTRL+ Shift+ End, quindi la soluzione dotNET è ottima, tranne che con il mio Excel 2010 ho bisogno di aggiungere un setse voglio evitare un errore:

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

e come verificarlo tu stesso:

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

Ecco i miei due centesimi.

IMHO il rischio di una riga nascosta con i dati esclusi è troppo significativo per xlUpessere considerato una risposta unica . Sono d'accordo che è semplice e funzionerà la maggior parte delle volte, ma presenta il rischio di sottovalutare l'ultima riga, senza alcun preavviso. Ciò potrebbe produrre risultati CATASTROFICI a favore di qualcuno che è saltato su Stack Overlow e stava cercando "un modo sicuro" per catturare questo valore.

Il Findmetodo è impeccabile e lo approverei come One Stop Answer . Tuttavia, lo svantaggio di modificare le Findimpostazioni può essere fastidioso, in particolare se fa parte di un UDF.

Le altre risposte pubblicate vanno bene, tuttavia la complessità diventa un po 'eccessiva. Quindi ecco il mio tentativo di trovare un equilibrio tra affidabilità, minima complessità e non utilizzo 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

Perché questo è buono:

  • Ragionevolmente semplice, non molte variabili.
  • Consente più colonne.
  • Non modifica le Findimpostazioni
  • Dinamico se utilizzato come UDF con l'intera colonna selezionata.

Perché questo è male:

  • Con insiemi di dati molto grandi e un enorme divario tra l'intervallo utilizzato e l'ultima riga nelle colonne specificate, questo funzionerà più lentamente, in rari casi SIGNIFICATAMENTE più lentamente.

Tuttavia, penso che una soluzione One-Stop che abbia uno svantaggio di incasinare le findimpostazioni o eseguire più lentamente sia una soluzione globale migliore. Un utente può quindi armeggiare con le proprie impostazioni per provare a migliorare, sapendo cosa sta succedendo con il proprio codice. L'utilizzo xLUpnon avviserà dei potenziali rischi e potrebbero continuare per chissà per quanto tempo non sapendo che il loro codice non funzionava correttamente.


1

Negli ultimi 3+ anni sono queste le funzioni che sto usando per trovare l'ultima riga e l'ultima colonna per colonna definita (per riga) e riga (per colonna):

Ultima colonna:

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

Ultima riga:

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

Nel caso dell'OP, questo è il modo per ottenere l'ultima riga nella colonna E:

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

Ultima riga, contando le righe vuote con i dati:

Qui possiamo usare le note formule di Excel , che ci forniscono l'ultima riga di un foglio di lavoro in Excel, senza coinvolgere VBA -=IFERROR(LOOKUP(2,1/(NOT(ISBLANK(A:A))),ROW(A:A)),0)

Per metterlo in VBA e non scrivere nulla in Excel, usando i parametri per queste ultime funzioni, potrebbe essere in mente qualcosa del genere:

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

Ciò restituirà un risultato errato se l'ultima riga / colonna è nascosta.
PGSystemTester,

@PGSystemTester - sì, ma a mio avviso, quando lo programmo, se è nascosto non è l'ultima colonna / riga necessaria.
Vityata,

Sono contento che funzioni per te. Sospetto che la tua situazione non sia un tipico caso d'uso. Più frequentemente quando lavoro con clienti che necessitano dell'ultima riga, cercano la cella più bassa con i dati, non la cella più bassa visibile con i dati. Comunque ... felice che funzioni. 👍
PGSystemTester

@PGSystemTester - Ho capito bene, ma prendersi cura della struttura e non consentire alle celle invisibili di funzionare come un incantesimo.
Vityata,

@PGSystemTester - sì, se l'attività consente eventualmente righe vuote, probabilmente utilizzerei EVAL()la famosa formula Excel. Anche se la gente può pensare che Eval()sia malvagio e questa è un'altra storia interessante su cui scrivere ...
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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.