TL;DR —
AutoFilterは条件に合わない行を隠します — 取り除きはしません。隠された それらの行はまだ範囲の中にあり、SUMにはまだ数えられ、素の.Copyにはまだコピーされます。 見えているものだけを読む・コピーする・削除するには、.SpecialCells(xlCellTypeVisible)を 通さなければなりません。そしてトグルの罠に注意 — 引数なしで.AutoFilterを呼ぶとフィルターの オン/オフが切り替わるので、「フィルターを設定する」マクロが 2 回目の実行でフィルターを オフにしてしまいます。まず.AutoFilterMode = Falseで状態をクリアしましょう。
' 表を Region = "West" でフィルターし、可視行だけに対して処理する。
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sales")
ws.AutoFilterMode = False ' まず残っているフィルターをすべてクリアする
With ws.Range("A1").CurrentRegion ' 見出しを含む表全体
.AutoFilter Field:=2, Criteria1:="West" ' Field は範囲の中での 1 始まり
' 可視データ行だけ(Offset(1) で見出しを飛ばす):
.Offset(1).SpecialCells(xlCellTypeVisible).EntireRow.Copy _
Destination:=Worksheets("West").Range("A1")
End With
ws.AutoFilterMode = False ' 終わったらフィルターを外す
AutoFilter は表を注目したい行だけに絞る最速の手段であり、Excel VBA で最も速い一括削除の
エンジンでもあります。ただし人をだますのは、行を取り除いたように見えて、実は隠しただけ
だからです。ほとんどすべての AutoFilter バグは、この 1 つの誤解の変奏です。だから本記事は
そこから始めます。
この記事で学べること
- 考え方の軸 — AutoFilter はビューであって削除ではない。隠れた行はまだそこにある
- なぜ
SUMと.Copyはフィルターで外れた行も含むのか、そしてSpecialCells(xlCellTypeVisible)がどう直すのか - 条件の設定 — 単一の値、比較演算子、2 条件、そして「リストのいずれか」
- トグルの罠 — なぜ引数なしの
.AutoFilter呼び出しがフィルターをオフにするのか - 数千行を高速に取り除く、プロの「フィルターしてから削除」パターン
- AutoFilter と FILTER 関数と Advanced Filter — どれに手を伸ばすべきか
考え方の軸:フィルターはビューであって削除ではない
フィルターを適用すると、Excel はたった 1 つのことをします — 一致しないすべての行に非表示 プロパティを立てるのです。データは動きません。何も取り除かれません。あなたは同じ範囲を、 一部の行を視界からさえぎるステンシル越しに見ているだけです。
それが仕組みのすべてで、初めて見ると意外な帰結を伴います — 範囲に対して働くものはすべて、
隠れた行をまだ見ています。=SUM(B2:B1000) はフィルターで外れた数値も足します。
Range("B2:B1000").Copy は隠れていようがすべてをコピーします。For Each cell ループは
1 つ残らず訪れます。フィルターが変えたのはあなたが見るものであって、範囲が持つものでは
ありません。
だから考え方の軸は、フィルターした表を操作する前に毎回自分に問う質問です — 全部の行が 欲しいのか、それとも見えている行だけか? 答えが「見えている行だけ」なら、範囲を直接 使うことはできません — Excel に可視部分集合を明示的に求めなければなりません。それが AutoFilter を信頼できるものにする、たった 1 つの習慣です。
最も大事なルール:可視行には SpecialCells を通して届く
フィルターが残した行だけに触れるには、範囲を可視セルに変換します。
Dim visible As Range
On Error Resume Next ' 可視セルが 1 つもないと xlCellTypeVisible は 1004 エラーを出す
Set visible = ws.Range("A1").CurrentRegion.Offset(1) _
.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not visible Is Nothing Then
' visible は複数領域の範囲。フィルターされた行は連続していない
MsgBox "Visible rows: " & visible.Rows.Count
End If
ここで体に叩き込むことが 2 つ。第一に、SpecialCells(xlCellTypeVisible) は複数領域の範囲を
返します — 可視行は隠れた行を挟んで散らばっているからです。ブロック単位で処理する必要が
あるときは For Each area In visible.Areas で反復するか、領域を自動で扱ってくれる .EntireRow
操作を使います。第二に、可視セルが 1 つもないと 1004 エラーを出します(0 行に一致した
フィルター)ので、On Error で守ります。SpecialCells を飛ばして生の範囲を操作すると、
黙って隠れた行まで一緒に処理してしまいます — AutoFilter で最もよくある間違いです。
条件を設定する:値・演算子・リスト
AutoFilter は Field(シートの列文字ではなく、フィルター範囲の中での 1 始まり)と、
1 つまたは 2 つの条件を取ります。
With ws.Range("A1").CurrentRegion
' 完全一致の値
.AutoFilter Field:=2, Criteria1:="West"
' 比較 - 演算子は文字列の中に入れる
.AutoFilter Field:=3, Criteria1:=">1000"
' 1 つのフィールドに 2 条件(100 以上 500 以下)
.AutoFilter Field:=3, Criteria1:=">=100", Operator:=xlAnd, Criteria2:="<=500"
' 「リストのいずれか」 - 配列に xlFilterValues を添える
.AutoFilter Field:=2, Criteria1:=Array("West", "East"), Operator:=xlFilterValues
End With
誰もが一度はまる罠 — Field は .AutoFilter を呼んだ範囲の左端から数えます。シートの
A 列からではありません。表が C 列から始まっていれば、Field:=1 は C 列です。そのオフセットを
間違えると、エラーひとつ出さずに違う列をフィルターしてしまいます。
トグルの罠:引数なしの AutoFilter はフィルターをオフにする
引数なしで呼んだ .AutoFilter はトグルです — フィルターの矢印がオフならオンに、オンなら
オフに切り替えます。だから 2 回以上実行されうるマクロの中では危険です。
ws.Range("A1").CurrentRegion.AutoFilter ' 1 回目: 矢印オン。もう 1 回: 矢印オフ。
同じ状態依存が、もっと分かりにくい形でも噛みつきます — 前回の実行で残った、あるいは ユーザーが手でかけたフィルターが、次にコードが範囲を読むときの「可視」の意味を静かに 変えてしまうのです。継承したフィルター状態を信用してはいけません。 まずクリアし、 終わったらまたクリアしましょう。
ws.AutoFilterMode = False ' 既存のフィルターをすべて外す - まっさらな状態に
' ... フィルターを適用し、作業をする ...
ws.AutoFilterMode = False ' シートを元のとおりに残す
If ws.AutoFilterMode Then をチェックすれば、現在フィルターが有効かどうかが分かります。
マクロを状態に依存しないものにすること — クリア、フィルター、作業、クリア — が、
「1 回目は動いたのに 2 回目で壊れた」類のバグを止めます。
覚えておくべきパターン:フィルターして、見えている行を削除する
これが AutoFilter が本格的なコードで居場所を得る理由です。条件に一致する数千行を削除するには、
フィルターして可視セルを削除するほうが、ループよりも劇的に高速です — マッチングは Excel が
やり、1 回の .Delete がすべてを取り除きます。
With ws.Range("A1").CurrentRegion
.AutoFilter Field:=5, Criteria1:="Cancelled"
' 可視データ行を削除 - Offset(1) で見出しを飛ばす。さもないと見出しも消える
On Error Resume Next
.Offset(1).SpecialCells(xlCellTypeVisible).EntireRow.Delete
On Error GoTo 0
End With
ws.AutoFilterMode = False
落とし穴は .Offset(1) です — これがないと見出し行も可視のままで、データと一緒に削除されて
しまいます。この「フィルターしてから削除」の手は VBA Delete Rows の
削除テクニックと自然に組み合わさります — そして一致件数が多いときは、私が逆順ループよりも
先に手を伸ばすやり方です。
AutoFilter と FILTER 関数と Advanced Filter
「フィルター」という語を共有しつつ、別々の問題を解く 3 つの道具があります。
- AutoFilter は一致しない行をその場で隠します。既存の表の部分集合を見る・コピー する・削除するときに使います。元データの上にかぶせたビューです。
FILTER()ワークシート関数は、一致する行の新しい配列をどこか別の場所にスピルさせ、 ライブで再計算します。自分自身で更新される数式駆動のリストが欲しいときに使います — マクロは 不要です。- Advanced Filter(
Range.AdvancedFilter)は、条件範囲を使って一致を別の場所にコピーしたり、 一意な値を抽出したりできます。複雑で多条件の抽出に使います。
判断はこうです。「この表を絞って一致に対して処理したい」なら AutoFilter が正解です。もし
別の場所にライブで自己更新する一致リストが欲しくなっている自分に気づいたら、マクロを書くのを
やめましょう — それは AutoFilter ではなく FILTER() 関数の仕事です。
ExcelMaster の活用
AutoFilter の罠はどれも静かなものです — フィルターした範囲を合計して隠れた行を含めてしまう、
コピーして全部取ってしまう、SpecialCells なしで削除して違うセルに当ててしまう、Field の
オフセット、2 回目の実行でフィルターをオフにするトグル。どれもエラーを出しません — ただ、
消えたと思っていた行に対して作用するだけです。
ExcelMaster なら、表に
何を見せたいかを言うだけです。「West 地域で 1000 超の行だけ残して」「Cancelled の注文を
すべて削除して」と説明すれば、まず古いフィルター状態をクリアし、Field のインデックスを
正しく取り、可視行だけに触れるものはすべて SpecialCells(xlCellTypeVisible) を通し、削除の
前にシートをバックアップします。ブックもコードもあなたの手元に残り、隠れた行が合計に
こっそり紛れ込む、あの一幕だけを飛ばせます。
よくある質問
VBA の AutoFilter はフィルターで外した行を削除する?
いいえ。AutoFilter は一致しない行を隠すだけです — それらは範囲の中に残り、SUM にはまだ
数えられ、.Copy にはまだコピーされ、ループにはまだ訪れられます。可視行だけを処理するには
.SpecialCells(xlCellTypeVisible) を通さなければなりません。実際に行を取り除くには、
フィルターしてから可視セルを削除します。
VBA で SUM がフィルターで外した行をまだ含むのはなぜ?
フィルターは行を隠すだけで取り除かないので、SUM は隠れた値も足すからです。可視行だけを
合計するには SUM の代わりに SUBTOTAL(109, range) を使うか、VBA なら
.SpecialCells(xlCellTypeVisible) を合計します。これはフィルターがビューであって削除では
ないことの、いちばん分かりやすい証拠です。
VBA の AutoFilter で 2 条件でフィルターするには?
Criteria1、Operator、Criteria2 を渡します。範囲には xlAnd を
(Criteria1:=">=100", Operator:=xlAnd, Criteria2:="<=500")、どちらか一方には xlOr を
使います。「リストのいずれかの値」には、配列を Criteria1 に渡して
Operator:=xlFilterValues を添えます。
マクロを再実行すると AutoFilter がオフになるのはなぜ?
引数なしで .AutoFilter を呼ぶのがトグルだからです — オフならオンに、オンならオフに
切り替えます。2 回実行するとフィルターが消えます。マクロを状態に依存しないものにしましょう —
まず ws.AutoFilterMode = False でクリアし、それから明示的な引数でフィルターを適用します。
VBA で条件によって行を削除する最速の方法は?
消したい行に一致するよう AutoFilter をかけ、それから可視セルを 1 回の呼び出しで削除します —
.Offset(1).SpecialCells(xlCellTypeVisible).EntireRow.Delete(Offset(1) が見出しを飛ばす)。
マッチングは Excel がやり、1 回の削除ですべてを取り除くので、1 行ずつループするより
はるかに高速です。
検証環境
動作確認: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-08。
関連ガイド: VBA Find · VBA Sort · VBA Delete Rows · VBA Last Row · VBA Range
