データが入力されている最後の行番号を知りたい場面はいくつかある。最後の行まで順番にデータを参照したり、最後の行の次の行から新たにデータを入力する場合などがある。
最後の行を取得する方法にもいくつかある。
Excel VBAでデータが入力されている最後の行番号を取得する方法【全パターン整理】
「最後の行」を取る方法は1つではありません。
どの列を見るのか、空白が混ざるのか、結合セルがあるのか、数式だけ入っているのか等で最適解が変わります。
この記事では、VBAで使われる手法を カテゴリ別に全網羅 します。
前提:どの「最後」を取りたいか?
最後の行番号には主に3種類あります。
- 列ベース:特定の列で、データがある最後の行(最も一般的)
- シート全体ベース:シートで使われている最後の行(UsedRangeなど)
- 範囲ベース:表(CurrentRegion / テーブル / 指定範囲)内の最後の行
1. Cells(Rows.Count, 列).End(xlUp)(最定番・最実務)
特定列の最終行(空白が途中にあっても、下端から上に見つける)。
Dim lastRow As Long
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
列番号でもOK。
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
注意点
- 対象列の下の方に「ゴミ(スペース、書式だけ等)」があるとズレることがあります。
Cells(Rows.Count, 列).End(xlUp) の意味を詳しく解説【最後の行取得の定番】
Excel VBAで「データが入っている最後の行」を取るとき、最も定番なのが次の書き方です。
Dim lastRow As Long
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
この1行でやっていることは、ざっくり言うと次の通りです。
「A列の一番下(最終行)から上方向に、最初に見つかるデータセルまでジャンプして、その行番号を返す」
1. まずは式を分解して読む
このコードは3段階に分解すると理解しやすいです。
Cells(Rows.Count, "A") 'A列の最終行のセル(例:A1048576)を指定
.End(xlUp) 'そこから上へCtrl+↑と同じ動作でジャンプ
.Row 'ジャンプ先セルの行番号を取り出す
2. Cells(Rows.Count, "A") の意味
Cells(行番号, 列) は「座標でセルを指定」
Cells(1, 1) 'A1
Cells(2, 1) 'A2
ここで使っている Rows.Count は そのシートで使える最大行数を返します。
- Excel 2007以降の通常シートなら 1,048,576 行
- したがって、
Cells(Rows.Count, "A")
はだいたい次の意味になります。
「A列の一番下のセル(A1048576)」
※列は "A" の代わりに 1 のような列番号でもOKです。
Cells(Rows.Count, 1) 'A列
3. .End(xlUp) の意味(Ctrl + ↑ と同じ)
.End(方向) は、Excelの操作で言うところの Ctrlキーを押しながら矢印キーで移動する動きです。
xlUp→ Ctrl + ↑xlDown→ Ctrl + ↓xlToLeft→ Ctrl + ←xlToRight→ Ctrl + →
つまり
Cells(Rows.Count, "A").End(xlUp)
は
「A1048576 から Ctrl + ↑ した場所」
を返します。
どういう場所に止まるの?
基本的には「連続したデータの端」に止まります。
- 下側が空白で、途中にデータがある → 最後のデータセルに止まる
- 列が全部空白 → A1 に止まる(ここが落とし穴)
4. .Row の意味
.End(xlUp) の戻り値は Range(セル)オブジェクトです。
そのセルの行番号だけ取り出すのが .Row です。
Dim r As Range
Set r = Cells(Rows.Count, "A").End(xlUp)
Debug.Print r.Address '例:$A$123
Debug.Print r.Row '例:123
5. “列Aの最終行” を取るということ
この手法が「列Aの最終行」と言われる理由はここです。
Cells(Rows.Count, "A")
で 列Aの一番下を起点にしているため、
最後の行は「列Aを基準にした最後の行」になります。
例えば、
- A列に必ずIDが入る設計 → この方法が最適
- A列が空白で、B列にデータがある → 期待より小さい行になることがある
6. 落とし穴と対策
落とし穴①:列が完全に空だと「1」になる
A列が完全に空だと、End(xlUp) は A1 に止まります。
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
'→ 1 になりやすい
対策(データなしなら0にする例)
Dim lastRow As Long
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
If WorksheetFunction.CountA(Columns("A")) = 0 Then
lastRow = 0
End If
落とし穴②:最終行の「データ」の定義が列依存
この方法は「指定した列」に依存します。
最終行判定に適した列(必ず埋まる列)を選ぶのがコツです。
落とし穴③:書式だけ・スペースでも“データ扱い”になり得る
セルにスペースだけ入っていたり、見えない文字があると最終行がズレます。
この場合は Find 方式や配列方式が安定します(別記事で詳解)。
7. 実務で安全な書き方(シート明示)
ActiveSheet依存を避けるならこう書くのが鉄板です。
Dim ws As Worksheet
Set ws = Worksheets("Sheet1")
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
まとめ
Cells(Rows.Count, "A").End(xlUp).Row は、
- A列の最下行セルを取って(A1048576)
- Ctrl + ↑ の動作で上へジャンプして
- 止まったセルの行番号を返す
という仕組みです。
次に「列Aが空白の可能性がある」「数式で空("")が返る」などの条件があるなら、
その条件に最適化した“最終行取得の決定版コード”も作れます。どんなデータ構造ですか?(例:ID列あり/空白あり/テーブル/CSVなど)
2. Range("A" & Rows.Count).End(xlUp)(1と同じ系統)
Dim lastRow As Long
lastRow = Range("A" & Rows.Count).End(xlUp).Row
3. Find(検索で最終セルを取る:値/数式/書式も条件指定可能)
「本当に最後に使われたセル」を柔軟に取りたいならFindが強いです。
値 or 数式が入っている最後の行
Dim lastRow As Long
Dim c As Range
Set c = Cells.Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious)
If Not c Is Nothing Then
lastRow = c.Row
Else
lastRow = 0
End If
値だけ(表示値ベース)に寄せる
Set c = Cells.Find(What:="*", LookIn:=xlValues, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious)
特定列だけでFindする
Set c = Columns("A").Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious)
If Not c Is Nothing Then lastRow = c.Row
4. UsedRange(シートの使用範囲ベース:速いがズレやすい)
Dim lastRow As Long
lastRow = ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row
注意点(超重要)
- 以前入力して消したセル、書式だけ残ったセルがあると 大きめにズレる ことがあります。
- “ユーザーが触った跡”も拾いがち。
5. CurrentRegion(「アクティブセル領域」の最後:表の塊ベース)
Dim lastRow As Long
lastRow = ActiveCell.CurrentRegion.Rows(ActiveCell.CurrentRegion.Rows.Count).Row
開始セルを明示する方が安全です。
Dim rg As Range
Set rg = Range("A1").CurrentRegion
lastRow = rg.Rows(rg.Rows.Count).Row
注意点
- 空白行/空白列があると領域が分断されます。
6. テーブル(ListObject)ならこれが最強(表が構造化されている前提)
Dim lastRow As Long
With ActiveSheet.ListObjects("Table1")
lastRow = .Range.Rows(.Range.Rows.Count).Row
End With
データ本体だけなら DataBodyRange。
Dim lastRow As Long
With ActiveSheet.ListObjects("Table1")
If Not .DataBodyRange Is Nothing Then
lastRow = .DataBodyRange.Rows(.DataBodyRange.Rows.Count).Row
Else
lastRow = .HeaderRowRange.Row 'データなし
End If
End With
7. SpecialCells(定数/数式セルを拾う:条件で「最後」を決められる)
定数(手入力値)だけの最後の行
Dim lastRow As Long
Dim rg As Range
On Error Resume Next
Set rg = Cells.SpecialCells(xlCellTypeConstants)
On Error GoTo 0
If Not rg Is Nothing Then
lastRow = rg.Areas(rg.Areas.Count).Rows(rg.Areas(rg.Areas.Count).Rows.Count).Row
Else
lastRow = 0
End If
数式セルだけの最後の行
Dim lastRow As Long
Dim rg As Range
On Error Resume Next
Set rg = Cells.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0
If Not rg Is Nothing Then
lastRow = rg.Areas(rg.Areas.Count).Rows(rg.Areas(rg.Areas.Count).Rows.Count).Row
Else
lastRow = 0
End If
注意点
- 範囲が複数Areaになるので「最後のArea=最後」とは限らないことがある(扱いに注意)。
8. CountAで「非空セル数」から推定(連続データ限定)
A列が途中で空白にならない前提の場合
Dim lastRow As Long
lastRow = WorksheetFunction.CountA(Columns("A"))
注意点
- 途中空白があると 最後の行ではなく件数 になります。
9. 配列に読み込んで判定(高速&柔軟:大量データ向け)
例:A列の最終行を「下から走査」
Dim lastRow As Long, i As Long
Dim v As Variant
v = Range("A1", Cells(Rows.Count, "A").End(xlUp)).Value
For i = UBound(v, 1) To 1 Step -1
If Len(v(i, 1) & vbNullString) > 0 Then
lastRow = i
Exit For
End If
Next
強み
- 「空白」「スペース」「数式結果が空」など、判定条件を自由に書ける
- 大量データでSelect/Endを多用するより安定しやすい
10. 最終セル(xlCellTypeLastCell)※非推奨(Excelが覚えている最後)
Dim lastRow As Long
lastRow = Cells.SpecialCells(xlCellTypeLastCell).Row
注意点(重要)
- “昔使っていた”セルも残るためズレやすい
- 仕様的に「最終行を取りたい」用途には不向き
どれを使うべき?(結論)
迷ったらこれ(列ベースの定番)
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
シート全体の「本当に最後」を取りたい(値/数式含む)
Set c = Cells.Find(What:="*", LookIn:=xlFormulas, SearchOrder:=xlByRows, SearchDirection:=xlPrevious)
If Not c Is Nothing Then lastRow = c.Row
テーブル運用ならこれ
lastRow = ActiveSheet.ListObjects("Table1").Range.Rows.Count + ActiveSheet.ListObjects("Table1").Range.Row - 1
よくある落とし穴
- 「書式だけ」入っているセルが下にある → End/UsedRangeがズレる
- 数式が入っているが表示は空("") → LookIn設定で結果が変わる
- 途中空白がある → CountAやCurrentRegionが期待通りにならない
必要なら、あなたのケース(例:A列にID、途中空白あり/数式あり/フィルタあり等)に合わせて、最も安全な1本に絞った実装例も出します。どの列・どんなデータ構造で「最後」を取りたいですか?