概要
Excel VBAを用いた業務自動化において、オートフィルタ(AutoFilter)は最も頻繁に使用される機能の一つです。しかし、フィルタリングした後に「現在、何件のデータが表示されているのか?」をVBAで判定する際、多くの初学者が躓きます。単に「Range.Rows.Count」を取得しても、それはフィルタ前の行数を含んでしまうためです。本記事では、Excelの組み込み関数であるSUBTOTAL関数をVBAから呼び出し、非表示行を除外して正確なカウントを取得する手法を、実務レベルのコードと共に詳細に解説します。
詳細解説
オートフィルタで抽出されたデータをカウントする際、VBAエンジニアが直面する最大の壁は「可視セル(Visible Cells)」の扱いです。Excelのシート上では視覚的にフィルタ結果が分かりますが、VBAのオブジェクトモデルにおいては、非表示行も依然として「存在する行」として扱われます。
一般的なアプローチとして「SpecialCells(xlCellTypeVisible)」を使用する方法がありますが、これにはデータ量が多い場合に実行速度が著しく低下するという弱点があります。数万行のデータに対してSpecialCellsを実行すると、メモリ消費が激しくなり、Excelが「応答なし」の状態に陥るリスクさえあります。
ここで推奨されるのが、ワークシート関数の「SUBTOTAL」を利用する方法です。SUBTOTAL関数には、引数に「103(COUNTA)」を指定することで、非表示行を無視して可視セルのみをカウントする機能が備わっています。VBAから「Application.WorksheetFunction.Subtotal」を呼び出すことで、極めて高速かつ安全に件数を取得することが可能です。
サンプルコード
以下に、実務でそのまま利用可能な標準的なコードを提示します。このコードは、指定したシートのリストをフィルタリングし、その結果の件数をメッセージボックスで表示するものです。
Sub GetFilteredCount()
Dim ws As Worksheet
Dim rngData As Range
Dim filteredCount As Long
' 対象シートの設定
Set ws = ThisWorkbook.Sheets("データ一覧")
' データ範囲の定義(ヘッダーを除いた範囲を指定するのがコツ)
Set rngData = ws.Range("A1").CurrentRegion
' オートフィルタの適用(例:A列で「売上」を抽出)
If ws.AutoFilterMode Then ws.AutoFilterMode = False
rngData.AutoFilter Field:=1, Criteria1:="売上"
' フィルタ結果の可視行数を取得
' SUBTOTALの103は、非表示行を除外したデータの個数(COUNTA)を意味する
' データ範囲の1列目(A列)を対象にカウントする
filteredCount = Application.WorksheetFunction.Subtotal(103, rngData.Columns(1).Cells)
' ヘッダー行を含んでカウントされる場合があるため、必要に応じて調整
' もし範囲がヘッダーを含む場合、結果から1を引くなどの考慮が必要
If filteredCount > 0 Then
MsgBox "抽出されたデータ件数は " & filteredCount - 1 & " 件です。", vbInformation
Else
MsgBox "対象データは見つかりませんでした。", vbExclamation
End If
' フィルタ解除
ws.AutoFilterMode = False
End Sub
実務アドバイス
実務でこの手法を用いる際、いくつか注意すべき「落とし穴」があります。
第一に、データ範囲の定義です。CurrentRegionを用いる際、空白行や空白列が隣接していると、意図しない範囲まで取得してしまうことがあります。可能な限り、最終行を取得する「Cells(Rows.Count, 1).End(xlUp).Row」のような手法と組み合わせ、動的に範囲を特定することを推奨します。
第二に、データが「ゼロ件」の場合の挙動です。SUBTOTALは抽出結果がゼロの場合、ヘッダー行のみが残っていても「1」を返します。この「1」をどう解釈するか(ヘッダーを含んでいるのか、データが1件あるのか)は、コードのロジックによって厳密に制御する必要があります。実務では、「filteredCount – 1」という計算を行うことで、データ件数のみを純粋に取り出すのが定石です。
第三に、パフォーマンスです。大規模なデータセット(10万行以上)を扱う場合、VBAから何度もセルを参照する処理は避けるべきです。一旦配列に格納してから処理を行うのが理想的ですが、件数カウントのみであれば、今回のSUBTOTAL手法が最もバランスの取れた高速解法となります。
また、もし「特定の条件に一致するセルを数える」のではなく、「合計値を取得したい」というニーズが発生した場合は、SUBTOTALの引数を「109(SUM)」に変更するだけで対応可能です。この柔軟性の高さも、SUBTOTAL関数をVBAで活用する大きなメリットです。
まとめ
Excel VBAにおけるフィルタ抽出後のカウント処理は、一見単純に見えて、実はExcelの内部仕様を理解しているかどうかが問われる深いテーマです。SpecialCellsの多用によるパフォーマンス劣化を避け、SUBTOTAL関数を活用することで、コードの可読性を保ちつつ、堅牢で高速なアプリケーションを構築することができます。
今回紹介したコードは、あくまで基本形です。実際の実務では、エラーハンドリング(フィルタ結果が空の場合の分岐処理)を加えたり、汎用的な関数として切り出したりすることで、より再利用性の高い資産となります。ぜひ、ご自身のプロジェクトでこの手法を取り入れ、より効率的な業務自動化を実現してください。VBAの学習において、こうした「組み込み関数をVBAからどう使うか」という視点は、レベルアップのための強力な武器になるはずです。
