1. はじめに
Excel VBAは導入しやすく、実務でも使用しやすい言語です。
そして比較的ゆるい言語でもあります。
その「ゆるさ」は、
初心者にとっては優しさであり、
一方で実務では 前提のズレに気づきにくい不具合 を引き起こす原因にもなります。
本記事では、Excel VBAで非常によく使われる
「最終行取得」 と 「フィルター(AutoFilter)」 によって起きる、
見落とされやすい挙動について整理します。
2. 最終行取得の基本動作
次のコードを見てください。
Dim lastRow As Long
lastRow = Cells(Rows.Count, 1).End(xlUp).Row
これは、最終行取得で最もよく見かけるコードです。
この処理は、実体としてはRows.Countはシート最下段の行、1はA列を示し、
End(xlUp)はキーボードでCtrl+↑を押下した場合と同じ効果となります。
3. 落とし穴:.End(xlUp) は非表示行で止まらない
AutoFilter などで行が非表示になっていると、End(xlUp) は非表示行では停止せず
次の「表示されているセル」までジャンプするという挙動になります。
4. 実務でよくある事例
フィルターが「手動で」かかったままになっている
実務で使われるExcelは、ユーザーが手動で AutoFilter を設定し、
特定条件で絞り込んだまま保存していることがしばしば見受けられます。
この状態を想定せずに最終行取得の処理を行った場合、
- データ自体は下まで存在する
- でもフィルターで末尾付近が非表示
- .End(xlUp) で取れるのは 「見えている最終行」
となり、取得すべき行が見落とされる事故につながります。
5. 厄介なAutofilterの仕様
1. ShowAllDataはフィルターが掛かっていないとエラーになる
以下はフィルター絞り込み解除のコードです。
Sub sumple()
Dim ws As Worksheet
Set ws = ActiveSheet
ws.ShowAllData ' 絞り込み解除
End Sub
絞り込みがかかっているか予想できないなら全件表示にしてしまえばいい、
と考えがちですが、上記コードを絞り込みでの非表示セルが無い状態で使用すると、
エラーで停止します。
2. AutoFilter メソッドはトグル的に動作する
絞り込まれているか予想できないならAutoFilter 設定自体を解除してしまえばいい、
と考えるかもしれません。
次のコードは、一見すると「フィルターを設定する処理」に見えます。
Range("A1").AutoFilter
しかしこのコードは、
-
フィルターが 未設定 → フィルターをかける
-
フィルターが 設定済み → フィルターを解除する
という トグル的な動作をします。
つまり、実行時点の状態によって結果が真逆になるという仕様です。
手動フィルターが残った状態でこのコードが実行されると、
- フィルターが解除される
- あるいは意図せず再設定される
といった挙動になります。
解除されれば良し、設定されても絞り込みにならないので、
最終行自体は取得できますが、設定を制御できていないので、
健全とは言い難いです。
6. 対策
フィルター状態を判定し、明示的に解除する
実務で事故を防ぐには、
最終行を取得する前に「状態を整える」 必要があります。
- フィルターには2つの判定が存在
AutoFilterMode
→ フィルター枠(▼)が存在するか
FilterMode
→ 実際に条件がかかり、行が絞り込まれているか
これを判定して、安全に解除します。
Sub sumple()
Dim ws As Worksheet
Set ws = ActiveSheet
If ws.AutoFilterMode Then
If ws.FilterMode Then
ws.ShowAllData ' 絞り込み解除
End If
End If
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
End Sub
このように、状態を確認してから明示的に解除し、
その後で最終行を取得することで、
実行のたびに結果が変わる問題を防げます。
7. まとめ
- 実務のExcelは「人が操作した状態」である
- フィルターが手動で残っていることは珍しくない
- AutoFilter はトグル動作するため、状態依存になりやすい
- 最終行取得前に状態を整えないと事故になる
VBAを書くときは、常に「今のシート状態は信用できない」
という前提で設計する必要があります。