VBAの技術的な内容は以前からあるベストプラクティス的なものだと思いますが、スマホで何となくGeminiに聞いてみたあと、『QIITAにコピーアンドペーストで投稿できるようなマークダウン形式(コード)』で改めて出力してと依頼したら、しっかり意図を汲んで出してくれました。
先人の方々もご経験あるお話だと思いますが個人的に感心した記念に残しておきます。
最初にこちらから聞いたこと
やりたいこと
エクセルVBAの処理方式を検討したい
現在の処理
- マクロ実行用ブックで操作
- 指定フォルダ内の全エクセルファイルを開き、テーブルを特定の条件3つでフィルタしてそれぞれ件数を確認
- 確認したファイル名と各件数を1行として結果シートに出力
補足
- 各テーブルに「パス」の列があり、『サイトアドレス』と『フォルダ』の結合文字列が各行に入っている
- テーブルはファイル格納場所一覧なので行ごとに同じ場合もあれば違う場合もある
- 『パス』の例
- /site/1/document/30_intermidiate
- /site/1/document/30_intermidiate/aaa001
- /site/1/document/30_intermidiate/aaa001/abc
- /site/1/document/30_intermidiate/aaa002
- /site/1/document/30_intermidiate/bb
- /site/
xの部分はエクセルファイル内で同一であり、エクセルファイル毎に異なる -
30_intermidiateの部分は全ファイル全行で同一 - フォルダ内のエクセルファイルは41件
- ファイルあたりのテーブル内行数は10万ぐらい
- 現在1ファイルあたり1分から2分ほど処理時間がかかっている
検討したい内容
- フィルタ後の集計件数を、さらに『パス』の
30_intermidiate後の第一階層フォルダごとに分けたい - 出力は各ファイル1行ではなく各ファイル複数行(第一階層フォルダ数)にしたい
- 出力行ごとに第一階層フォルダ名もだしたい
- 第一階層フォルダなし(30_intermidiateでパスが終わりの場合)は第一階層フォルダブランクとして扱う
- 処理時間はできる限り伸ばしたくない
重要
- VBAコード生成がメインではなく、効率のよい情報取得方法を、いくつか考えてほしい。
- アルゴリズム的な面での比較検討も必要なら含めてほしい。
- 例
- 『パス』を全行取得して重複削除後に第一階層フォルダまでのdictionaryを作成して、フィルタ(AutoFilter)後の件数取得処理に第一階層フォルダごとのカウントアップを加える(処理が遅そう。。。)
- テーブル全体に対してVBAでクエリを投げて第一階層ごとの件数を取得(dictionary化は最初に実行しないとダメか?)
- ほか何でも
- 例になくても良い方法をいろいろ提案して
以下、いったん普通に回答が出力されたあとに、こちらからコピーアンドペースト用のマークダウンで再出力をお願いして、出てきたマークダウンコードをそのままコピーアンドペーストしたもの
410万行のExcel集計をVBAで高速化したい!手法別の比較検討まとめ
背景・現状
大量のExcelファイル(41ファイル、各10万行)から特定の条件でデータを集計する処理の最適化を検討しています。
現在の処理内容
- マクロ実行用ブックから指定フォルダ内の全エクセルファイルを順次開く。
- 各シートのテーブルに対して3つの条件でフィルタをかけ、件数をカウント。
- 結果を1ファイル1行として集計シートに出力。
- 課題: 1ファイルあたり1〜2分かかっており、さらに集計軸(フォルダ階層別)を細分化したいが、処理時間は伸ばしたくない。
データの特性
- 「パス」列に
/site/x/document/30_intermidiate/フォルダA/フォルダBのような形式で文字列が格納されている。 -
30_intermidiateまでは共通、その後の「第一階層(上記例ではフォルダA)」ごとに集計を行いたい。
提案:効率的な情報取得のための3つのアプローチ
「処理時間を伸ばしたくない」という目的を達成するため、Excelの描画やセル操作(AutoFilter)を極力排除する3つの手法を検討しました。
1. 配列と連想配列(Dictionary)によるメモリ内高速集計
【方針】シート操作を一切やめ、データをすべてメモリ(配列)に読み込んで処理する。
-
仕組み:
- テーブル範囲を
Variant配列に一括で取り込む。 - 配列をループで回し、
If文で3つの条件判定を行う。 - 同時に「パス」列から「第1階層フォルダ名」を抽出。
-
「第1階層名」をキー、「件数(配列)」を値とした
Dictionaryオブジェクトに加算。
- テーブル範囲を
-
メリット: * 圧倒的に高速。セル操作(AutoFilter)に比べ数百倍の速度向上が見込める。
- 集計軸が動的に増えてもDictionaryで柔軟に対応可能。
2. ADO (SQL) による「ファイルを開かない」集計
【方針】各Excelファイルをデータベースとして扱い、SQL文で集計結果のみを吸い出す。
-
仕組み:
- 各ファイルに対してADOコネクションを張る。
-
GROUP BYを含むSQL文を実行し、集計済みレコードセットを取得する。
-
メリット: * 低負荷: ExcelファイルをUIとして完全に開かないため、メモリ消費が少ない。
- 一括集計: 集計ロジックをSQL側に任せられる。
3. Power Query (パワークエリ) によるETL処理
【方針】VBAではなく、Excel標準のデータ接続・変換機能を利用する。
-
仕組み:
- 「フォルダーから」データ取得を選択し、41ファイルを一括結合。
- クエリ内でパスの分割、条件フィルタ、グループ化(集計)を定義。
-
メリット: * 並列処理: バックグラウンドで最適化された処理が行われる。
- メンテナンス性: 集計ロジックの変更がGUI上で完結する。
各手法の比較検討
| 比較項目 | ① 配列 + Dictionary | ② ADO (SQL) | ③ Power Query |
|---|---|---|---|
| 処理速度 | 非常に高速(メモリ依存) | 高速(Disk I/O依存) | 高速(最適化される) |
| 実装難易度 | 中(VBAの知識が必要) | 高(SQLの知識が必要) | 低(GUI操作がメイン) |
| ファイル展開 | 内部で開く | 開かない | 内部で処理 |
| 拡張性 | ロジック変更はコード修正 | SQLの変更が必要 | GUIで容易に変更可能 |
| 推奨環境 | メモリに余裕がある場合 | PCスペックを抑えたい場合 | モダンなExcel運用をしたい場合 |
推奨案の深掘り: 「配列 + Dictionary」
現状のVBA資産を活かしつつ、最も確実に速度を改善できるのは 「1. 配列と連想配列」 の組み合わせです。
効率化のための「パス」解析ロジック
第1階層フォルダの抽出は、Split 関数よりも InStr で位置を特定する方が大規模データでは効率的です。
-
30_intermidiate/の位置(終端)をInStrで特定。 - その位置以降にある最初の
/を探す。 -
/が見つかればその間が「第1階層」、見つからなければ「第1階層なし(ブランク)」と判定。
パフォーマンス改善の期待値
現在の「1ファイル1~2分」の主因は、「10万回×3条件のAutoFilter更新」と「可視セルのカウント(SpecialCellsなど)」 という重い描画・アクセス処理にあります。
これを「配列読み込み(0.1秒)+配列ループ処理(0.2秒程度)」に置き換えることで、1ファイル数秒程度まで短縮できる可能性があります。
まとめ
大規模なExcelデータの集計では、**「いかにセルに触れないか」**が高速化の鍵となります。
まずは「配列+Dictionary」でメモリ上での集計を試み、もしファイルサイズが大きすぎてメモリ不足になる場合は「ADO」や「Power Query」への移行を検討するのがベストなステップです。