- 技術 💻
VBAの最終行取得|End・UsedRange・Findの違い早見表
VBAの最終行取得は、必ず埋まる列が1つある表なら Cells(Rows.Count, 列).End(xlUp).Row、どの列にも空白がありうる表なら Find でシートの後ろから検索する、の2つを使い分けます。 取得方法はほかにもあり、それぞれ「何を最後とみなすか」が違います。
この記事では、7つの方式が同じ表でどの行を返すかを条件別の早見表にまとめ、そのまま貼って使える最終行・最終列の関数を載せます。
確認環境:Microsoft Learn の Excel VBA リファレンス(Range.End、Worksheet.UsedRange、Range.CurrentRegion、Range.Find、Range.SpecialCells、Range.MergeArea、ListObject、ListRows、Worksheet.FilterMode、Worksheet.ShowAllData、Integer・Long データ型、Overflow (Error 6))と Microsoft サポートの「ワークシートの最後のセルを特定してリセットする」を 2026-10-06 に開いて確認しました。「結果」を付けたコードと早見表の行番号は、LibreOffice 24.2.7.2(Linux)の Basic を VBA 互換モード(Option VBASupport 1)にして動かした値です。
この記事の結論
- 1列が必ず埋まる表(No や ID の列がある表)は、その列で
ws.Cells(ws.Rows.Count, 列).End(xlUp).Rowを使います。 - どの列にも空白がありうる表は、
ws.Cells.Find("*", ..., SearchDirection:=xlPrevious)でシート全体を後ろから探します。 - 行番号は Long 型で受けます。 Integer は 32,767 までなので、それより下の行でオーバーフロー(エラー 6)になります。
- 見出しだけの表は「0件」として扱う分岐を入れます。 End(xlUp) は見出し行の番号を返すので、そのまま使うと見出しをデータとして読んでしまいます。
- UsedRange は「使用済み範囲」の最後です。塗りつぶしだけのセルも含むので、値がある最後の行とは一致しないことがあります。
7方式の早見表
| 方式 | 書き方(最終行) | 何を最後とみなすか | 向いている表 |
|---|---|---|---|
| End(xlUp) | ws.Cells(ws.Rows.Count, 1).End(xlUp).Row |
指定した列を一番下から上へたどって最初に当たるセル | 必ず埋まる列がある表 |
| End(xlDown) | ws.Range("A1").End(xlDown).Row |
指定したセルから下へたどって、連続したデータが切れる手前 | 途中に空白がない列 |
| UsedRange | ws.UsedRange.Row + ws.UsedRange.Rows.Count - 1 |
シートの使用済み範囲の最後の行 | 書式も含めた範囲を知りたいとき |
| CurrentRegion | ws.Range("A1").CurrentRegion の最後の行 |
空白行と空白列で囲まれた範囲の最後の行 | 空白行で区切られた表の1つ分 |
| Find | ws.Cells.Find("*", SearchDirection:=xlPrevious).Row |
シート全体で、後ろから探して最初に見つかるセル | どの列にも空白がありうる表 |
| SpecialCells | ws.Cells.SpecialCells(xlCellTypeLastCell).Row |
使用済み範囲の最後のセル | UsedRange と同じ使いどころ |
| ListObject | lo.HeaderRowRange.Row + lo.ListRows.Count |
テーブルのデータ行の最後 | テーブルとして書式設定した表 |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「VBAの最終行取得|End・UsedRange・Findの違い早見表」
条件別:どの行を返すかの早見表
次の表をシートに作り、条件を1つずつ変えて各方式の結果を調べました。
| A | B | C | |
|---|---|---|---|
| 1 | No | 品名 | 備考 |
| 2 | 1 | 品1 | メモ |
| 3 | 2 | 品2 | |
| 4 | 3 | (空白) | |
| 5 | 4 | 品4 | |
| 6 | 5 | 品5 |
A列はすべて埋まり、B列は4行目が空白、C列は2行目だけに値があります。
| 条件 | End(xlUp) A列 | End(xlUp) B列 | End(xlDown) A1から | End(xlDown) B1から | UsedRange | CurrentRegion | Find |
|---|---|---|---|---|---|---|---|
| 上の表のまま(B4 が空白) | 6 | 6 | 6 | 3 | 6 | 6 | 6 |
| 4行目を丸ごと空白にし、7行目までデータを入れる | 7 | 7 | 3 | 3 | 7 | 3 | 7 |
| 5〜6行目を非表示にする | 4 | 3 | 4 | 3 | 6 | 6 | 解除してから使う |
| フィルターで5〜6行目が隠れている | 4 | 3 | 4 | 3 | 6 | 6 | 解除してから使う |
| 10行目に塗りつぶしだけを設定する | 6 | 6 | 6 | 3 | 10 | 6 | 6 |
| A7:A9 に空文字を返す数式を入れる | 9 | 6 | 9 | 3 | 9 | 9 | 9 |
| 見出しだけ(データ0件) | 1 | 1 | 1048576 | 1048576 | 1 | 1 | 1 |
| 何も入っていないシート | 1 | 1 | 1048576 | 1048576 | 1 | 1 | Nothing |
| A7 に値を入れて A7:A8 を結合する | 7 | 7 | 7 | 3 | 7 | 7 | 7 |
- 数字は、確認環境に書いた VBA 互換モードで動かして得た行番号です。CurrentRegion は A1 を起点にし、Find は LookIn:=xlFormulas で検索しています。
- 手元の Excel で同じ表を出すには、後半の「確認用マクロ」をそのまま貼って動かします。 自分のブックの表で試すと、どの方式が合うかをすぐに見比べられます。
- 非表示行とフィルター中の Find は、リファレンスに隠れたセルの扱いが書かれていないため、表には入れていません。絞り込みや非表示を解除してから取得する形にすると、方式によらず同じ行が得られます(書き方は後述)。
表から読み取れること
| 条件 | 気をつける方式 | 理由 |
|---|---|---|
| 列の途中に空白がある | End(xlDown) | 空白の手前(3行目)で止まる |
| 空白行で表が分かれている | End(xlDown)、CurrentRegion | 最初のかたまりの最後(3行目)を返す |
| 非表示行・フィルター中 | End(xlUp)、End(xlDown) | 見えている行の中での最後(4行目、3行目)を返した |
| 書式だけのセルがある | UsedRange | 書式だけの行(10行目)まで含む |
| 空文字を返す数式がある | End(xlUp)、UsedRange、CurrentRegion、Find | 数式が入った行(9行目)を返す |
| データ0件 | End(xlDown) | シートの最終行(1048576)まで進む |
| 一番下が結合セル | End(xlUp) ほか | 結合範囲の先頭の行(7行目)を返す |
SpecialCells(xlCellTypeLastCell) と ListObject の位置づけ
この2つは、リファレンスの記載をもとに整理します。
| 方式 | リファレンス・サポートの記載 | 読み取れること |
|---|---|---|
| SpecialCells(xlCellTypeLastCell) | Range.SpecialCells の例に「Sheet1 の使用済み範囲の最後のセルを選択する」とある。Microsoft サポートには、空のセルに書式があると、最後のセルがデータのある範囲の外になることがあると書かれている | 使用済み範囲の右下のセルを指す。書式だけのセルも含まれる |
| ListObject.Range | テーブルが適用されている範囲を返す | 見出し・データ(集計行を表示していれば集計行)を含むテーブル全体 |
| ListObject.DataBodyRange | 見出し行を除いた値の範囲を返す | データ部分だけ |
| ListObject.HeaderRowRange | 見出し行の範囲を返す | 見出しの行番号が分かる |
| ListObject.ListRows.Count | テーブルの行(ListRow)の数を返す | データの件数が分かる。0 ならデータ0件 |
End(xlUp):必ず埋まる列がある表の定番
Range.End は、リファレンスで「元の範囲を含む領域の端のセルを返す。End+方向キーを押すのと同じ」と説明されています。シートの一番下のセルから上向きにたどるのが、最終行取得の定番の形です。
Sub SampleEndUp()
Dim ws As Worksheet
Dim lastRow As Long
Set ws = ThisWorkbook.Worksheets("Sheet1")
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Debug.Print lastRow
End Sub
| 部分 | 意味 |
|---|---|
ws.Rows.Count |
シートの行数(1048576)。Excel の仕様では 1,048,576 行 × 16,384 列 |
ws.Cells(ws.Rows.Count, 1) |
A列の一番下のセル |
.End(xlUp) |
そこから上へたどって最初に当たるセル |
.Row |
そのセルの行番号 |
Cells と Rows.Count の両方にシートを付けます。 ws.Cells(Rows.Count, 1) のように片方だけにすると、Rows.Count はその時点で前面にあるシートを見にいきます。両方に ws. を付けておくと、どのシートが前面にあっても同じ結果になります。
End(xlUp) は指定した列だけを見ます。早見表の「B列」のとおり、最後の行のB列が空白だと、B列で取った最終行は表の最終行より小さくなります。基準にする列は、No や ID のように必ず埋まる列を選びます。
End(xlDown):連続したデータの切れ目まで
lastRow = ws.Range("A1").End(xlDown).Row
上から下へたどるので、途中に空白があるとそこで止まります。 早見表では、B列(4行目が空白)で 3 を返しました。また、見出しだけの表では下にデータがないため、シートの最終行(1048576)まで進みます。
End(xlDown) が合うのは、「空白が出るまでのひとかたまり」を取りたい場面です。表全体の最終行には End(xlUp) が向いています。
UsedRange・SpecialCells:使用済み範囲の最後
Worksheet.UsedRange は、シートの使用済み範囲を表す Range を返します。使用済み範囲が1行目から始まるとは限らないので、開始行に行数を足して 1 を引きます。
lastRow = ws.UsedRange.Row + ws.UsedRange.Rows.Count - 1
lastCol = ws.UsedRange.Column + ws.UsedRange.Columns.Count - 1
ws.UsedRange.Rows.Count だけだと「使用済み範囲の行数」なので、表が3行目から始まるシートでは最終行より 2 少ない値になります。
早見表のとおり、10行目に塗りつぶしだけを設定すると UsedRange の最終行は 10 になりました。値がある最後の行(6)とは別のものです。Microsoft サポートのページでは、不要な書式が残った範囲を消す手順として、データより先の行や列を選んで[すべてクリア]を行い、ブックを保存する方法が案内されています。
CurrentRegion:空白行・空白列で囲まれた表
Range.CurrentRegion は、リファレンスで「空白行と空白列の任意の組み合わせで囲まれた範囲」と説明されています。
Dim tbl As Range
Set tbl = ws.Range("A1").CurrentRegion
lastRow = tbl.Row + tbl.Rows.Count - 1
lastCol = tbl.Column + tbl.Columns.Count - 1
- 列の途中に空白セルがあっても、その行のほかの列に値があれば、表はつながったものとして扱われます(早見表1行目:B4 が空白でも 6)。
- 丸ごと空白の行があると、そこで表が切れます(早見表2行目:3)。
- リファレンスには、保護されたワークシートではこのプロパティを使えないと書かれています。
1枚のシートに、空白行で区切って複数の表を置いているときに、表1つ分の範囲を取る用途に合います。
Find:どの列にも空白がありうる表
Find でワイルドカード * をシートの後ろから探すと、列を決めずに「何か入っている最後のセル」が見つかります。
Sub SampleFind()
Dim ws As Worksheet
Dim c As Range
Dim lastRow As Long
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set c = ws.Cells.Find(What:="*", After:=ws.Range("A1"), LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious)
If c Is Nothing Then
lastRow = 0
Else
lastRow = c.Row
End If
Debug.Print lastRow
End Sub
| 引数 | 指定 | 理由 |
|---|---|---|
| What | "*" |
何か入っているセルを探す |
| After | ws.Range("A1") |
検索は After の次のセルから始まる。逆方向なので、A1 の手前、つまりシートの末尾から探しはじめる |
| LookIn | xlFormulas |
数式が入ったセルも対象にする |
| LookAt | xlPart |
部分一致 |
| SearchOrder | xlByRows(最終行)/xlByColumns(最終列) |
行の順に探すか、列の順に探すか |
| SearchDirection | xlPrevious |
後ろから前へ |
リファレンスには、次の2点が書かれています。
- 見つからないときは Nothing を返す。 何も入っていないシートでは Nothing になるので、
.Rowを付ける前にIs Nothingで分岐します。 - LookIn・LookAt・SearchOrder・MatchByte の設定は保存され、次回省略すると前回の値が使われる。 毎回すべて書いておくと、直前の検索に左右されません。
最終列の取得
最終列は、行と列を入れ替えた形で書けます。
| 方式 | 書き方 | 結果(サンプル表) |
|---|---|---|
| End(xlToLeft) | ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column |
3 |
| Find | SearchOrder を xlByColumns にして .Column を取る |
3 |
| UsedRange | ws.UsedRange.Column + ws.UsedRange.Columns.Count - 1 |
3 |
End(xlToLeft) は、見出し行(1行目)で取るのが定番です。 データ行には空白の列があっても、見出しはすべての列に入っていることが多いためです。見出しがない表や、行ごとに列数が違う表は Find を使います。
コピペ用:最終行・最終列の関数
標準モジュールに貼って使える4つの関数です。どれも Long 型で返し、シートを引数で受け取り、データがないときは 0 を返します。
Option Explicit
' 値がある最後の行(指定した列)。見出しだけ・空のシートは 0 を返す
Public Function LastRowInCol(ByVal ws As Worksheet, ByVal col As Long, _
Optional ByVal headerRow As Long = 1) As Long
Dim r As Long
r = ws.Cells(ws.Rows.Count, col).End(xlUp).Row
If r <= headerRow Then
LastRowInCol = 0
Else
LastRowInCol = r
End If
End Function
' 値がある最後の行(シート全体)。どの列に空白があってもよい
Public Function LastRowInSheet(ByVal ws As Worksheet, _
Optional ByVal headerRow As Long = 1) As Long
Dim c As Range
Set c = ws.Cells.Find(What:="*", After:=ws.Range("A1"), LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious)
If c Is Nothing Then
LastRowInSheet = 0
ElseIf c.Row <= headerRow Then
LastRowInSheet = 0
Else
LastRowInSheet = c.Row
End If
End Function
' 値がある最後の列(指定した行)。その行が空なら 0 を返す
Public Function LastColInRow(ByVal ws As Worksheet, ByVal r As Long) As Long
Dim c As Long
c = ws.Cells(r, ws.Columns.Count).End(xlToLeft).Column
If c = 1 And IsEmpty(ws.Cells(r, 1).Value) Then
LastColInRow = 0
Else
LastColInRow = c
End If
End Function
' 値がある最後の列(シート全体)。空のシートは 0 を返す
Public Function LastColInSheet(ByVal ws As Worksheet) As Long
Dim c As Range
Set c = ws.Cells.Find(What:="*", After:=ws.Range("A1"), LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious)
If c Is Nothing Then
LastColInSheet = 0
Else
LastColInSheet = c.Column
End If
End Function
シートの状態を変えながら呼び出した結果です。
| シートの状態 | LastRowInCol(ws, 1) | LastRowInSheet(ws) | LastColInRow(ws, 1) | LastColInSheet(ws) |
|---|---|---|---|---|
| 何も入っていない | 0 | 0 | 0 | 0 |
| 見出し(A1:C1)だけ | 0 | 0 | 3 | 3 |
| A列は6行目まで、B列は5行目まで、C列は見出しだけ、E3 に値 | 6 | 6 | 3 | 5 |
3つ目の状態では、LastRowInCol(ws, 2) が 5、LastRowInCol(ws, 3) が 0、LastColInRow(ws, 3) が 5、LastColInRow(ws, 9) が 0 でした。列や行を指定する関数は、その列・その行だけを見ていることが分かります。
使い方:0件を分岐し、次の行に書く
Sub AppendSample()
Dim ws As Worksheet, n As Long
Set ws = ThisWorkbook.Worksheets("Sheet1")
n = LastRowInCol(ws, 1)
If n = 0 Then n = 1 ' 見出しだけのときは見出しの次の行から書く
ws.Cells(n + 1, 1).Value = "追加"
End Sub
見出しだけのシートで動かすと A2 に、6行目までデータがあるシートで動かすと A7 に「追加」と入りました。
データを読む側では、0 のときに処理を抜けます。
n = LastRowInCol(ws, 1)
If n = 0 Then
MsgBox "データがありません"
Exit Sub
End If
For i = 2 To n
' 1行ずつの処理
Next
0件の分岐がないと、For i = 2 To 1 となってループは回りませんが、ws.Range("A2:A" & n) のように範囲を組み立てる書き方では、A2:A1 という見出しを含む範囲になります。0件を先に分けておくと、どちらの書き方でも同じ動きになります。
Integerで受けるとオーバーフローする例
A列の 40000 行目に値があるシートで、最終行を Integer と Long で受けて比べました。
Function OverflowSample() As String
Dim ws As Worksheet, n As Integer, m As Long
On Error GoTo EH
Set ws = ThisWorkbook.Worksheets(1)
ws.Cells(40000, 1).Value = "x"
m = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
OverflowSample = "Long: " & m & vbLf
n = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
OverflowSample = OverflowSample & "Integer: " & n & vbLf
Exit Function
EH:
OverflowSample = OverflowSample & "Integer: エラー " & Err.Number & vbLf
End Function
結果:
Long: 40000
Integer: エラー 6
| 型 | 範囲(リファレンスの記載) | 行番号を受けられるか |
|---|---|---|
| Integer | -32,768 〜 32,767 | 32,767 行目まで |
| Long | -2,147,483,648 〜 2,147,483,647 | シートの最終行(1,048,576)まで入る |
エラー 6 は、リファレンスで「代入先の制限を超える代入をしようとしたときに起きる」と説明され、対処として「より広い範囲の値を持てる型の変数に代入する」が挙げられています。行番号・列番号・ループのカウンターは Long で宣言しておくと、データが増えても同じコードで動きます。
値がある最後の行と、使用済み範囲の最後の行
「最終行」という言葉は、次の3つのどれを指すかで使う方式が変わります。
| 指すもの | 説明 | 取り方 |
|---|---|---|
| 値か数式がある最後の行 | 空文字を返す数式の行も含む | End(xlUp)、Find |
| 表示される値がある最後の行 | 空文字を返す数式の行は含めない | End(xlUp) のあと、値が空の行を上へ戻る |
| 使用済み範囲の最後の行 | 書式だけの行も含む | UsedRange、SpecialCells(xlCellTypeLastCell) |
空文字を返す数式を飛ばす
A7:A9 に =IF(B7="","",B7) を入れると、見た目は空でもセルには数式があります。表示される値がある最後の行を取りたいときは、End(xlUp) の結果から上へ戻ります。
' 空文字を返す数式を飛ばして、表示される値がある最後の行を返す
Public Function LastValueRowInCol(ByVal ws As Worksheet, ByVal col As Long, _
Optional ByVal headerRow As Long = 1) As Long
Dim r As Long
r = ws.Cells(ws.Rows.Count, col).End(xlUp).Row
Do While r > headerRow
If Len(CStr(ws.Cells(r, col).Value)) > 0 Then Exit Do
r = r - 1
Loop
If r <= headerRow Then
LastValueRowInCol = 0
Else
LastValueRowInCol = r
End If
End Function
A7:A9 に上の数式を入れた状態で、LastRowInCol(ws, 1) は 9、LastValueRowInCol(ws, 1) は 6 でした。
一番下が結合セルのとき
結合セルの値は先頭のセルに入るので、End(xlUp) は結合範囲の先頭の行を返します。Range.MergeArea は、指定したセルを含む結合範囲を返すプロパティです(結合されていなければ、そのセル自身を返します)。これで結合範囲の最後の行を求めます。
' 一番下が結合セルでも、結合範囲の最後の行を返す
Public Function LastRowWithMerge(ByVal ws As Worksheet, ByVal col As Long, _
Optional ByVal headerRow As Long = 1) As Long
Dim c As Range, r As Long
Set c = ws.Cells(ws.Rows.Count, col).End(xlUp)
r = c.MergeArea.Row + c.MergeArea.Rows.Count - 1
If r <= headerRow Then
LastRowWithMerge = 0
Else
LastRowWithMerge = r
End If
End Function
A7:A8 を結合した状態で、LastRowInCol(ws, 1) は 7、LastRowWithMerge(ws, 1) は 8 でした。
非表示行・フィルター中のシートで取得する
早見表では、非表示行やフィルターで隠れた行があると、End(xlUp) は見えている行の中での最後を返しました。隠れた行も含めた最終行がほしいときは、次のどちらかにします。
| やり方 | 書き方 | 使うプロパティ・メソッド |
|---|---|---|
| フィルターを解除してから取る | If ws.FilterMode Then ws.ShowAllData |
FilterMode(フィルターモードなら True)、ShowAllData(絞り込まれたリストのすべての行を表示する) |
| フィルターの範囲から求める | ws.AutoFilter.Range の最後の行 |
AutoFilter.Range(オートフィルターが適用されている範囲) |
Sub LastRowOnFilteredSheet()
Dim ws As Worksheet, lastRow As Long
Set ws = ThisWorkbook.Worksheets("Sheet1")
If ws.FilterMode Then ws.ShowAllData
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Debug.Print lastRow
End Sub
FilterMode、ShowAllData、AutoFilter.Range は、Microsoft Learn のリファレンスに載っている定義に沿って書いています。早見表のとおり、UsedRange と CurrentRegion は、隠れた行があっても 6 を返しました。
テーブル(ListObject)の最終行
[テーブルとして書式設定]した表は、ListObject から行数を取れます。列を決めたり、空白を気にしたりせずに済みます。
' テーブルのデータの最後の行。データ0件なら 0 を返す
Public Function LastRowOfTable(ByVal lo As ListObject) As Long
If lo.ListRows.Count = 0 Then
LastRowOfTable = 0
Else
LastRowOfTable = lo.HeaderRowRange.Row + lo.ListRows.Count
End If
End Function
Sub SampleTable()
Dim lo As ListObject
Set lo = ThisWorkbook.Worksheets("Sheet1").ListObjects(1)
Debug.Print lo.ListRows.Count ' データの件数
Debug.Print LastRowOfTable(lo) ' データの最後の行
End Sub
ListObjects、ListRows.Count、HeaderRowRange、DataBodyRange は、Microsoft Learn のリファレンスに載っている定義に沿って書いています。見出し行を表示しているテーブルを前提にした式です。行を追加するときは、リファレンスの例にある lo.ListRows.Add で末尾に1行増やせます。
確認用マクロ:手元のブックで5方式を見比べる
自分の表でどの方式が何行目を返すかを、一度に出すマクロです。
' 5方式の結果を1行ずつ文字列にまとめる
Public Function LastRowReport(ByVal ws As Worksheet, ByVal col As Long) As String
Dim s As String, c As Range
s = "End(xlUp) : " & ws.Cells(ws.Rows.Count, col).End(xlUp).Row & vbCrLf
s = s & "End(xlDown) : " & ws.Cells(1, col).End(xlDown).Row & vbCrLf
s = s & "UsedRange : " & ws.UsedRange.Row + ws.UsedRange.Rows.Count - 1 & vbCrLf
s = s & "CurrentRegion : " & ws.Range("A1").CurrentRegion.Row + _
ws.Range("A1").CurrentRegion.Rows.Count - 1 & vbCrLf
Set c = ws.Cells.Find(What:="*", After:=ws.Range("A1"), LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious)
If c Is Nothing Then
s = s & "Find : Nothing"
Else
s = s & "Find : " & c.Row
End If
LastRowReport = s
End Function
Public Sub ShowLastRowReport()
Debug.Print LastRowReport(ActiveSheet, 1)
End Sub
サンプル表で LastRowReport(ws, 2)(B列を指定)を呼んだ結果です。
End(xlUp) : 6
End(xlDown) : 3
UsedRange : 6
CurrentRegion : 6
Find : 6
ShowLastRowReport は、この文字列を Debug.Print でイミディエイト ウィンドウに出すだけの入口です。
どの方式を選ぶかの判断表
| 表の状態・目的 | 選ぶ方式 |
|---|---|
| No や ID など、必ず埋まる列がある | End(xlUp)(その列で) |
| どの列にも空白がありうる | Find(後ろから検索) |
| テーブルとして書式設定している | ListObject(ListRows.Count) |
| 空白行で区切った表の1つ分を取りたい | CurrentRegion |
| 空白が出るまでのひとかたまりを取りたい | End(xlDown) |
| 書式も含めて、使われている範囲を知りたい | UsedRange、SpecialCells(xlCellTypeLastCell) |
| 空文字を返す数式が下まで入っている | End(xlUp) のあと、値が空の行を上へ戻る |
| フィルター中・非表示行がある | 解除してから End(xlUp)、または UsedRange |
確かめて分かったこと
- End(xlUp) は列ごとに結果が違いました。 同じ表でも、A列は 6、C列(2行目だけ値がある)は 2 です。基準の列の選び方で結果が決まります。
- 見出しだけの表では、End(xlUp) が 1、End(xlDown) が 1048576 を返しました。 どちらも「データの最終行」ではないので、0件の分岐が要ります。
- 何も入っていないシートでも、UsedRange は A1(1行目)を返しました。 行数 0 にはならないので、UsedRange だけでは空のシートを見分けられません。Find は Nothing を返すので見分けられます。
- 塗りつぶしだけの行で変わったのは UsedRange だけでした。 End・CurrentRegion・Find は 6 のままです。
- 空文字を返す数式は、End(xlUp)・UsedRange・CurrentRegion・Find のどれでも「入っている行」として数えられました。
- 非表示行があると、End(xlUp) と End(xlDown) は見えている行で止まりました。 UsedRange と CurrentRegion は隠れた行も含めた 6 を返しました。
よくある質問
Q. End(xlUp) と End(xlDown) の違いは? A. たどる向きが違います。End(xlUp) はシートの一番下から上へたどるので途中の空白に左右されず、End(xlDown) は上から下へたどるので最初の空白の手前で止まります。表全体の最終行には End(xlUp) が向いています。
Q. UsedRange と CurrentRegion の違いは? A. UsedRange はシート全体の使用済み範囲、CurrentRegion は指定したセルを含む、空白行と空白列で囲まれた範囲です。UsedRange はワークシートのプロパティ、CurrentRegion は Range のプロパティです。
Q. 最終行が実際より大きくなるときは? A. 空文字を返す数式か、書式だけのセルが下にあることが多いです。End(xlUp) や Find は数式のある行を、UsedRange は書式だけの行も数えます。数式を飛ばすには、値が空の行を上へ戻る LastValueRowInCol の形にします。
Q. 空白がある列で最終行を取得するには? A. 必ず埋まる別の列で End(xlUp) を使うか、Find でシート全体を後ろから検索します。Find は列を決めずに探すので、どの列に空白があっても表の最終行が取れます。
Q. 最終行の変数は Integer でもよいですか? A. Long にします。Integer は 32,767 までで、それより下の行番号を代入するとエラー 6(オーバーフロー)になります。
まとめ
- 必ず埋まる列があるなら End(xlUp)、どの列にも空白がありうるなら Find を使います。
CellsとRows.Countの両方にシートを付けると、前面のシートに左右されません。- 行番号は Long で受けます。 Integer は 32,767 行目までです。
- 見出しだけ・空のシートは 0 件として分岐します。End(xlUp) は見出し行を、End(xlDown) はシートの最終行を返します。
- UsedRange と SpecialCells(xlCellTypeLastCell) は使用済み範囲の最後で、書式だけのセルも含みます。
- 空文字を返す数式、結合セル、非表示行は、それぞれ「上へ戻る」「MergeArea で広げる」「解除してから取る」で対応できます。
- テーブルは ListRows.Count で件数が取れ、列や空白を気にせずに済みます。
参考・出典
- Microsoft Learn「Range.End property (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.range.end
- Microsoft Learn「Worksheet.UsedRange property (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.worksheet.usedrange
- Microsoft Learn「Range.CurrentRegion property (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.range.currentregion
- Microsoft Learn「Range.Find method (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.range.find
- Microsoft Learn「Range.SpecialCells method (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.range.specialcells
- Microsoft Learn「Range.MergeArea property (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.range.mergearea
- Microsoft Learn「ListObject object (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.listobject
- Microsoft Learn「ListRows object (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.listrows
- Microsoft Learn「Worksheet.FilterMode property (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.worksheet.filtermode
- Microsoft Learn「Worksheet.ShowAllData method (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.worksheet.showalldata
- Microsoft Learn「AutoFilter.Range property (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.autofilter.range
- Microsoft Learn「Integer data type」 https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/integer-data-type
- Microsoft Learn「Long data type」 https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/long-data-type
- Microsoft Learn「Overflow (Error 6)」 https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/overflow-error-6
- Microsoft サポート「Locate and reset the last cell on a worksheet」 https://support.microsoft.com/en-us/office/locate-and-reset-the-last-cell-on-a-worksheet-c9e468a8-0fc3-4f69-8038-b3c1d86e99e9
- Microsoft サポート「Excel specifications and limits」 https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
(いずれも 2026-10-06 に確認)
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「VBAの最終行取得|End・UsedRange・Findの違い早見表」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。