「データの最終行って、どうやって取得すればいいの?」
VBAでマクロを書き始めると、必ずぶつかるのがこの疑問ですよね。行数が毎回変わるデータを処理するには、最終行を自動で取得する方法を知っておく必要があります。
でも安心してください。VBAで最終行を取得する方法はいくつかあります。仕組みを理解すれば迷わず使いこなせますよ。この記事では代表的な3つの方法をコード付きで解説します。それぞれの違い・使い分け・よくあるトラブルの対処法もまとめてお伝えしますね。
VBAで最終行を取得する方法3選
最終行を取得する方法は複数ありますが、ここでは実務でよく使われる3つの方法を紹介します。
End(xlUp)で最終行を取得する(最もよく使う方法)
まずはVBAで最終行を取得する定番コードです。
Dim lastRow As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
このコードは「A列の最終行」を取得しています。仕組みをかんたんに説明すると、こんな流れです。
Rows.Countでシートの最大行数(1,048,576行)を取得するCells(1048576, 1)でA列の一番下のセルを指定する.End(xlUp)で、そこから上方向にデータが入っているセルを探す.Rowで、見つかったセルの行番号を数値で返す
これはExcelで Ctrl + 上矢印キー を押したときと同じ動きです。シートの一番下から上に向かってデータを探すので、途中に空白セルがあっても正確に最終行を見つけられます。
取得した最終行は変数 lastRow に格納しておくと、あとからFor文やDo Loopで繰り返し使えて便利です。変数の基本的な使い方については「VBAの変数の使い方とルールについて解説」で詳しく紹介しています。
列を変更したいとき は、Cells(Rows.Count, 1) の数値部分を変えてください。
'--- B列の最終行を取得 ---
lastRow = Cells(Rows.Count, 2).End(xlUp).Row
'--- C列の最終行を取得 ---
lastRow = Cells(Rows.Count, 3).End(xlUp).Row
A列=1、B列=2、C列=3……と、列番号を数値で指定します。セルの指定方法について詳しく知りたい方は「RangeとCellsはどう違う?使い分けを実例で解説」を参考にしてみてください。
なお、Cells ではなく Range を使った書き方もあります。Range オブジェクトの基本操作は「VBAでのRangeの使い方を基礎から解説」で紹介しています。あわせてチェックしてみてください。
NOTE
Rows.Countはワークシートの最大行数を自動で返してくれます。Excel 2007以降のファイル形式(.xlsx / .xlsm)では1,048,576行、Excel 2003以前の形式(.xls)では65,536行です。Rows.Countを使えばファイル形式に関係なく正しい最大行数が取得できるので、数値を直接書く必要はありません。
UsedRangeで最終行を取得する
2つ目の方法は UsedRange プロパティを使う方法です。
Dim lastRow As Long
lastRow = ActiveSheet.UsedRange.Rows.Count + ActiveSheet.UsedRange.Row - 1
UsedRange はワークシートで「使用されている範囲」を返すプロパティです。データが入力されている範囲全体を自動で判定してくれます。
ここで気になるのが + ActiveSheet.UsedRange.Row - 1 の部分ですよね。これは 1行目が空白のケースに対応するため の計算です。
たとえば、A1〜A3が空白で、A4からデータが始まっているケースを考えてみましょう。
UsedRange.Rows.Count→ 使用範囲の行数を返す(例: 7行)UsedRange.Row→ 使用範囲の開始行番号を返す(例: 4行目)
もし Rows.Count だけで取得すると、結果は「7」になります。でも実際のデータ最終行はA10(4 + 7 – 1 = 10行目)ですよね。だから開始行を足して1を引く補正が必要なんです。
'--- 1行目からデータが始まっている場合はシンプルでもOK ---
lastRow = ActiveSheet.UsedRange.Rows.Count
1行目からデータが始まっていることが確実であれば、上のシンプルな書き方でも問題ありません。ただし、他の人が使うマクロでは、念のため完全版の計算式を使っておくと安心です。
注意点として、UsedRange は 書式が設定されているだけのセル も「使用済み」とみなします。背景色や罫線だけ入っているセルも対象です。そのため、データの最終行よりも大きな値が返ることがあります。
SpecialCellsで最終行を取得する
3つ目は SpecialCells メソッドを使う方法です。
Dim lastRow As Long
lastRow = Cells.SpecialCells(xlCellTypeLastCell).Row
SpecialCells(xlCellTypeLastCell) は、ワークシートで最後に使用されたセルを返します。これはExcelで Ctrl + End を押したときと同じ動作です。
この方法も UsedRange と同様に、書式のみのセルも考慮されるため、実際のデータの最終行よりも大きな値が返る場合があります。
'--- 最終行と最終列を同時に取得 ---
Dim lastRow As Long
Dim lastCol As Long
lastRow = Cells.SpecialCells(xlCellTypeLastCell).Row
lastCol = Cells.SpecialCells(xlCellTypeLastCell).Column
最終行と最終列を同時に取得できるのが、この方法の便利なポイントです。
ただし1つ注意点があります。データが1つもないシート で SpecialCells を実行すると、実行時エラー(Runtime Error 1004)が発生します。空シートでも安全に動くようにするには、エラーハンドリングを入れておきましょう。
Dim lastRow As Long
On Error Resume Next
lastRow = Cells.SpecialCells(xlCellTypeLastCell).Row
On Error GoTo 0
If lastRow = 0 Then
lastRow = 1 'データなしの場合は1行目を返す
End If
On Error Resume Next でエラーを一時的にスキップし、直後の On Error GoTo 0 で通常のエラー処理に戻しています。
3つの方法の違いと使い分け
ここまで紹介した3つの方法を比較表で整理してみましょう。
| 項目 | End(xlUp) | UsedRange | SpecialCells |
|---|---|---|---|
| 取得対象 | 指定した列のデータ最終行 | シート全体の使用範囲 | シート全体の使用範囲 |
| 空白セルの影響 | 受けにくい | 受けにくい | 受けにくい |
| 書式のみのセル | 無視する | 含む | 含む |
| 特定の列を指定 | できる | できない | できない |
| 空シートでの動作 | 1を返す | エラーなし | エラーが発生する |
| Excel操作での同等機能 | Ctrl + 上矢印 | なし | Ctrl + End |
| おすすめ度 | 最もおすすめ | 状況による | 状況による |
結論:迷ったら End(xlUp) を使いましょう。 特定の列に対して正確な最終行を取得でき、書式だけのセルに惑わされないため、実務で最も信頼性が高い方法です。
UsedRange や SpecialCells は「シート全体でどこまでデータがあるか」をざっくり把握したい場面で役立ちます。たとえば、データの範囲を一括でコピーしたいときなどに使うと便利ですよ。
シート指定を明示して誤動作を防ぐ
ここまでのコード例では、対象シートを明示せずに Cells(Rows.Count, 1) と書いていました。この書き方は暗黙的に ActiveSheet(現在アクティブなシート)を参照します。
シートが1つだけのマクロなら問題ありません。ただし 複数シートを扱うマクロ では、意図しないシートの最終行を取得してしまうことがあります。
安全なのは、With ステートメントで対象シートを明示する書き方です。
Sub シート指定で最終行を取得()
Dim lastRow As Long
With ThisWorkbook.Worksheets("データ一覧")
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
End With
MsgBox "最終行: " & lastRow
End Sub
ポイントは .Cells や .Rows.Count の先頭にドット(.)をつけること。このドットが With で指定したシートを参照していることを示します。ドットを忘れると ActiveSheet が参照されてしまうので注意してください。
'--- よくある間違い ---
With ThisWorkbook.Worksheets("データ一覧")
lastRow = Cells(Rows.Count, 1).End(xlUp).Row 'ドットなし → ActiveSheetを参照してしまう
End With
'--- 正しい書き方 ---
With ThisWorkbook.Worksheets("データ一覧")
lastRow = .Cells(.Rows.Count, 1).End(xlUp).Row 'ドットあり → 指定シートを参照
End With
シンプルなマクロでは省略しても構いません。ただ、シートを明示しておくと「どのシートを処理しているか」が一目瞭然になります。あとから見返したときにも読みやすいので、習慣にしておくのがおすすめです。
VBA 最終行の取得でよくあるトラブルと対処法
最終行の取得はシンプルに見えますが、いくつかハマりやすいポイントがあります。事前に知っておけば慌てずに済むので、チェックしておきましょう。
空白セルがあると正しく取得できない
End(xlUp) ではなく End(xlDown) を使うと、空白セルがある場合に正しく最終行を取得できません。
'--- End(xlDown) を使った場合(非推奨) ---
lastRow = Cells(1, 1).End(xlDown).Row
このコードはA1セルから下方向にデータを探します。一見すると自然な発想ですが、ここに落とし穴があります。
途中に空白セルがあると、そこで止まってしまうんです。たとえば A1〜A5 にデータがあり、A6 が空白、A7〜A10 にもデータがあるケースで考えてみましょう。End(xlDown) は A5 で止まり、本当の最終行 A10 を取得できません。
さらに厄介なのが、データが1行しかない場合 です。A1にしかデータがないと、A2以降がすべて空白なので、シート最終行の1048576行目まで突き抜けてしまいます。
'--- 安全な方法(End(xlUp)を使う) ---
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
下から上に探す End(xlUp) なら、途中の空白セルに影響されません。基本的には End(xlUp) を使うクセ をつけておくのがおすすめです。
取得した値が想定と違うときの確認ポイント
「コードは正しいはずなのに、取得した最終行が想定と違う……」というときは、次の3点を確認してみてください。
- 対象の列は正しいか —
Cells(Rows.Count, 1)の列番号(2つ目の引数)が意図した列になっているか確認しましょう。A列のつもりでB列を指定していた、というミスは意外とよくあります。
- データが入っていない列を指定していないか — 指定した列にデータが1つも入っていない場合、End(xlUp) は1行目を返します。変数に格納した値が「1」になっていたら、この可能性を疑ってみてください。
- 書式だけのセルが残っていないか — UsedRange や SpecialCells を使っている場合は要注意です。過去にデータを削除したセルに書式が残っていると、実際のデータより大きな値が返ります。セルを選択して「クリア」(Delete キーではなく「すべてクリア」)を実行すると解消できます。
実務で使える!最終行取得の活用例
最終行を取得する方法がわかったら、実際のマクロで使ってみましょう。ここでは実務でよく使うパターンを3つ紹介します。
For文と組み合わせて全行を処理する
最終行取得の最大の活用場面は、For文と組み合わせたループ処理 です。
Sub 全行処理()
Dim lastRow As Long
Dim i As Long
'--- A列の最終行を取得 ---
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
'--- 2行目から最終行までループ処理 ---
For i = 2 To lastRow
'--- B列に「処理済み」と入力する例 ---
Cells(i, 2).Value = "処理済み"
Next i
MsgBox lastRow - 1 & "件のデータを処理しました"
End Sub
For i = 2 To lastRow とすることで、データが何行あっても自動で全行を処理してくれます。2行目から開始しているのは、1行目がヘッダー行(見出し行)であることを想定しているためです。
For文の使い方について詳しくは「For~Next文の使い方と実務で役立つ応用テクニック」で解説しています。ループ処理をもっと深く知りたい方はぜひ読んでみてくださいね。
Do Loopで条件付きの繰り返し処理をする
「すべての行」ではなく 特定の条件を満たすまで ループしたいときは、Do Loop が便利です。たとえば、A列を上から順に見ていき、「完了」と書かれたセルが見つかったらループを抜ける処理はこう書けます。
Sub 条件付きループ()
Dim lastRow As Long
Dim i As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
i = 2 'ヘッダーの次の行から開始
Do While i <= lastRow
If Cells(i, 2).Value = "完了" Then
MsgBox i & "行目で「完了」が見つかりました"
Exit Do
End If
i = i + 1
Loop
End Sub
For文は「全行を処理する」のが得意ですが、Do Loop は「条件に応じてループを制御する」のが得意です。最終行取得と組み合わせることで、「最終行を超えないように安全にループする」ことができます。
If文での条件分岐については「VBAのIf文の使い方を基礎から解説」を参考にしてみてください。Do Loopの詳しい書き方は「Do Loopの使い方を基礎から解説」で紹介しています。
最終列を取得する方法
行だけでなく、最終列 を取得したい場面もありますよね。考え方は最終行の取得とまったく同じです。
Dim lastCol As Long
lastCol = Cells(1, Columns.Count).End(xlToLeft).Column
違いは以下の3点です。
Rows.Count→Columns.Count(最大列数を取得)End(xlUp)→End(xlToLeft)(左方向に探す).Row→.Column(列番号を返す)
最終行と最終列を両方取得すれば、データ範囲全体を動的に指定できます。
Sub データ範囲を取得()
Dim lastRow As Long
Dim lastCol As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
lastCol = Cells(1, Columns.Count).End(xlToLeft).Column
'--- データ範囲を選択する例 ---
Range(Cells(1, 1), Cells(lastRow, lastCol)).Select
MsgBox "データ範囲: A1:" & Cells(lastRow, lastCol).Address
End Sub
End プロパティで指定できる方向を一覧にしておきます。
| 定数 | 方向 | 用途 |
|---|---|---|
| xlUp | 上方向 | 最終行の取得 |
| xlDown | 下方向 | 先頭行からの探索(非推奨) |
| xlToLeft | 左方向 | 最終列の取得 |
| xlToRight | 右方向 | 先頭列からの探索 |
まとめ
VBAで最終行を取得する3つの方法を紹介しました。
- End(xlUp) — 最もおすすめ。特定の列の正確な最終行を取得できる
- UsedRange — シート全体の使用範囲を取得。書式のみのセルも含む点に注意
- SpecialCells — Ctrl+Endと同じ動作。最終行と最終列を同時に取得したいときに便利
迷ったら Cells(Rows.Count, 1).End(xlUp).Row を使えば間違いありません。
最終行の取得は、VBAでマクロを作るうえで避けて通れない基本テクニックです。最初のうちはコードを見ながら書いても問題ありません。何度も使ううちに自然と覚えられるので、焦らず実務のなかで身につけていきましょう。
For文やDo Loopと組み合わせれば、データの自動処理がぐっとラクになりますよ。まずは今回紹介したコードをコピーして、ご自身のマクロで試してみてください。
VBAの学習を体系的に進めたい方は「VBAの学習順番を初心者向けに解説」も参考にしてみてくださいね。
