大量のデータを絞り込むには、オートフィルタが便利です。ここでは、VBAからオートフィルタを使い倒すテクニックをご紹介します。なお、ボリュームがありますので、以下の項目にページを分けて解説します。なお、ここで解説するオートフィルタは、通常のワークシートに設定されているとします。テーブルでオートフィルタを操作するときは、また別の考え方が必要です。VBAでテーブルを扱う方法に関しては「VBAでテーブルの操作」をご覧ください。
ここで解説する"集計"とは、主に"カウント"のことです。マクロでオートフィルタを活用するときは、絞り込んだ結果が何件あるかが重要になってきます。たとえば、下図の表を「A列が"田中"である」で絞り込み、その結果をSheet2のセルA1にコピーするとします。絞り込んだ結果をコピーする考え方は「絞り込んだ結果をコピーする」をご覧ください。
Sub Macro1() With Range("A1") .AutoFilter 1, "田中" .CurrentRegion.Copy Sheets("Sheet2").Range("A1") .AutoFilter End With End Sub
結果は、こうなります。
そう、実はA列に"田中"は1件もなかったんです。これでは、コピーする必要がありません。こんなときは、次のように考えたいです。
Sub Macro1() With Range("A1") .AutoFilter 1, "田中" If 絞り込んだ結果 > 0 Then .CurrentRegion.Copy Sheets("Sheet2").Range("A1") .AutoFilter End If End With End Sub
絞り込んだ結果が何件あるかは、要するに「表示されているセルが何個あるか」です。だったら、次のように調べることも可能です。
Sub Macro2() MsgBox Range(Range("A2"), Cells(Rows.Count, 1).End(xlUp)).SpecialCells(xlCellTypeVisible).Count End Sub
でもこれ、めんどくさくないですか?上記のコードを、スッと迷わずに書ける人ばかりではないでしょう。実は、こんなときはワークシート関数を使うと一発で分かるんです。使うのはSUBTOTAL関数です。SUBTOTAL関数に関しては「この関数はこう使え[SUBTOTAL関数]」をご覧ください。計算の種類に指定する数値の、1番台と100番台の違いも解説しています。
Sub Macro3() With Range("A1") .AutoFilter 1, "田中" If WorksheetFunction.Subtotal(3, Range("A:A")) > 1 Then .CurrentRegion.Copy Sheets("Sheet2").Range("A1") .AutoFilter End If End With End Sub
注意していただきたいのは、SUBTOTAL関数の結果が「1より大きかったら」と判定しているところです。「0」ではありません。SUBTOTAL関数に指定している範囲は「A列全体(Range("A:A"))」です。ということは、ここにはタイトル行も含まれます。だから、SUBTOTAL関数の結果が「1」だったら、それは「タイトル行だけ」つまり「"田中"はない」ということです。
基本的な考え方は以上です。
何となく世間では「VBAができるとExcelの上級者」みたいに思われる節があります。まったく違います。まるで違います。ぜんぜん違います。「俺、VBAできるからExcel上級者だしw ピボットテーブルとか使わないしw 俺VBAできるからw 関数?ああ知らないしw 俺VBAできるからw」なんて。もうね、へそがプーアール茶を沸かしますw これは、車の運転(ドライビング)に例えるなら、次のように言っているのと同じです。「俺、左折できるからドライビング上級者だしw 右折とかしないしw 俺左折できるからw 車庫入れ?ああ知らないしw 俺左折できるからw」みたいなw そしてこの人は、どこへ目指すにも左折だけで行こうとします。われわれは、左折の大会に出るのではありません。VBAのコンテストに出るんじゃないんです。Excelという道具を使って、実務を遂行するのが目的です。だったらVBAだけじゃなくて、機能や関数も使えなかったら仕事になりません。こんなこと、言うまでもないことです。
だから、柔軟な発想をしてください。上記のように、オートフィルタで絞り込んだ結果が何件あるかは、ワークシート関数を使えば一発です。もし、VBAしかできなくて、SUBTOTAL関数の存在を知らなかったら、さっきのマクロは作れません。
先の「A列を"田中"で絞り込んだ結果」が0件だったらコピーしないというマクロは、次のような考え方でした。
これだと、たとえA列に"田中"が存在しなくても、とりあえず1回はオートフィルタかけます。存在しないんだったら、そもそもオートフィルタ自体無駄ですね。だったら、次のように考えることもできます。
A列に"田中"が存在するかどうかなんて、そんなもん簡単に分かります。VBAだけでやってみましょうか。
Sub Macro4() Dim FoundCell As Range Set FoundCell = Range("A:A").Find(What:="田中", Lookat:=xlWhole) If FoundCell Is Nothing Then MsgBox "田中は存在しません" Exit Sub Else With Range("A1") .AutoFilter 1, "田中" .CurrentRegion.Copy Sheets("Sheet2").Range("A1") .AutoFilter End With End If End Sub
もちろん、これでもできます。でもね、A列に"田中"が存在するかどうかなんて、Findステートメントを使うまでもなく、ワークシート関数で一発です。
Sub Macro5() If WorksheetFunction.CountIf(Range("A:A"), "田中") = 0 Then MsgBox "田中は存在しません" Exit Sub Else With Range("A1") .AutoFilter 1, "田中" .CurrentRegion.Copy Sheets("Sheet2").Range("A1") .AutoFilter End With End If End Sub
マクロで、どこかの範囲に、何かのデータが"存在するかどうか"を判定することはよくあります。そんなとき、COUNTIF関数が最強です。For Nextで回して1件ずつ調べたら時間がかかります。Findステートメントはオブジェクト変数を宣言しなければなりませんし、何よりも「Is Nothing」という判定をしなければなりません。COUNTIF関数は存在しないと0を返します。つまり、存在しなくてもエラーにならないんです。その分コードが短く、シンプルになります。
ほかのケースで考えてみましょうか。
さきほど例にした上図の表から、次のような集計をしてみましょう。
実務ではこういうとき、元の表とは別シートに集計することが多いですが、ここでは解説を簡単にするため、同じシート上に集計するとします。まずは、オートフィルタだけでやってみます。
Sub Macro6() Dim i As Long For i = 2 To 13 Range("A1").AutoFilter 1, Cells(i, 5) Range("A1").AutoFilter 2, Cells(i, 6) Cells(i, 7) = WorksheetFunction.Subtotal(3, Range("A:A")) - 1 Next i Range("A1").AutoFilter End Sub
まぁ、これでも十分短いコードですが。でもね、[名前]が"広瀬"で、[記号]が"A"の件数って、それCOUNTIFS関数で分かりますよね。
だから、次のコードでいけます。
Sub Macro7() With Range("G2:G13") .Value = "=COUNTIFS(A:A,E2,B:B,F2)" .Value = .Value End With End Sub
同じ結果になります。もちろん、COUNTIFS関数じゃなくてSUMIFS関数を使えば合計も計算できます。
じゃ、今度は下図のような集計だったら。
これも考え方は一緒です。いかに、ワークシート関数のことや、絶対参照/相対参照や、複数セルへの一括入力という機能などを知っているかがポイントです。VBAじゃありません。
Sub Macro8() With Range("F2:H5") .Value = "=COUNTIFS($A:$A,$E2,$B:$B,F$1)" .Value = .Value End With End Sub
Excelは「機能・関数・VBA」という3つの要素で構成されています。これら3つの要素をすべて使えるのが理想です。「VBAはできるけどワークシート関数は知らない」とか「ピボットテーブルは得意だけどVBAは苦手」のように、どれかしか使えないと、Excelのポテンシャルを最大限に引き出すことはできません。ぜひExcelの「機能・関数・VBA」をすべてマスターしてください。ちなみに、すべて完璧に分かっている者から言わせていただくと、VBAが一番簡単です。最も難しいのはダントツでワークシート関数です。間違いありません。