Access VBAの極致:DoCmdに頼らない「爆速」Excelエクスポート術
こんにちは。現場で泥臭い自動化と格闘し、システムを最適化し続けているエンジニアです。
Accessを使っていて「`DoCmd.TransferSpreadsheet` でExcel出力したら、なぜか遅いし細かな調整が効かない……」と頭を抱えたことはありませんか?実は、それ、Accessの「本来の力」を使いこなせていないだけかもしれません。
今回は、DAOの`QueryDef`と`Recordset`を組み合わせ、メモリ上でデータを捌き、Excelへ高速に流し込む「プロの定石」を伝授します。これさえ習得すれば、Access VBAの景色がガラリと変わりますよ。
—
なぜ DoCmd は「遅い」のか?
`DoCmd.TransferSpreadsheet` は非常に便利ですが、内部でファイルを開いたり閉じたり、AccessとExcelの間で過剰な通信が発生したりと、オーバーヘッド(無駄な待ち時間)が非常に大きいのです。
一方、今回紹介する手法は、「DAOでデータをメモリに吸い上げ、ExcelのCOMオブジェクト経由で一気にセルへ流し込む」というアプローチです。これを覚えると、数万件のデータ出力も劇的に速くなります。
—
実践:QueryDef と Recordset を使った高速エクスポート
まずは、以下のコードを見てください。これが「現場の標準」です。
Public Sub ExportQueryToExcel_Fast()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
Dim xlApp As Object
Dim xlBook As Object
Dim xlSheet As Object
Set db = CurrentDb
‘ QueryDefを取得(パラメータクエリにも対応可能)
Set qdf = db.QueryDefs(“qry_売上データ”)
‘ ここでパラメータを動的にセットすることも可能
‘ qdf.Parameters(“[Forms]![frm_Menu]![txtStartDate]”) = “2023/04/01”
Set rs = qdf.OpenRecordset(dbOpenSnapshot)
‘ レコードが存在しない場合のガード
If rs.EOF Then
MsgBox “対象データがありません。”
GoTo Cleanup
End If
‘ Excelを起動
Set xlApp = CreateObject(“Excel.Application”)
Set xlBook = xlApp.Workbooks.Add
Set xlSheet = xlBook.Sheets(1)
‘ ヘッダーの書き込み
Dim i As Integer
For i = 0 To rs.Fields.Count – 1
xlSheet.Cells(1, i + 1).Value = rs.Fields(i).Name
Next i
‘ データの一括転送(RangeへのCopyFromRecordsetが最強の秘訣)
‘ 1セルずつ書き込むのは素人。一気にメモリから流し込むのがプロ。
xlSheet.Range(“A2”).CopyFromRecordset rs
xlApp.Visible = True
Cleanup:
‘ オブジェクトの解放(メモリリークを防ぐ鉄則)
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
Set qdf = Nothing
Set db = Nothing
Set xlSheet = Nothing: Set xlBook = Nothing: Set xlApp = Nothing
End Sub
—
ここがプロのポイント!
1. `CopyFromRecordset` を使う
このメソッドこそが高速化の核心です。1セルずつ `Cells(i, j).Value = …` とループで書き込んでいると、処理時間はデータ量に比例して爆発的に増えます。`CopyFromRecordset` は、RecordsetのデータをメモリブロックとしてExcelのセル範囲に一気にコピーするため、体感速度が桁違いです。
2. `dbOpenSnapshot` の活用
`OpenRecordset` を開く際、デフォルトではなく `dbOpenSnapshot` を指定してください。これは「読み取り専用の静的なデータ」としてメモリに保持するため、編集の必要がないエクスポート処理において、最も軽量で高速なモードです。
3. オブジェクトの「行儀よい」解放
初心者の方で意外と忘れがちなのが、`Set … = Nothing` による解放です。Access VBAはガベージコレクションが強力ではありません。特にExcelのインスタンス(`xlApp`)を放置すると、タスクマネージャーにExcelが残る「ゴーストプロセス」が発生し、システムを徐々に重くします。終了処理は必ず書きましょう。
—
よくあるトラブルと解決策
- 「データが多すぎてExcelの行数制限を超える」
- 解決策:`rs.GetRows` を使い、Excelの最大行数(1,048,576行)ごとにシートを分割するロジックを組むのが正攻法です。
- 「型変換エラーが出る」
- 解決策:`QueryDef` であらかじめ書式を整えておくか、Access側で `Format()` や `CStr()` を使い、Excelが解釈しやすい形式(特に日付や数値)にキャストしてから投げましょう。
—
最後に:自動化の先にあるもの
この手法をマスターすれば、Accessは単なる「データベース」ではなく、「高度なデータ処理エンジン」に変貌します。
「動かないから何となくボタンを押す」段階から、「データがどうメモリを流れ、どうExcelに焼き付けられるか」をイメージできる段階へ。この一段階上の視点を持つだけで、あなたの書くコードの質は劇的に向上します。
もしコードが動かない、あるいはもっと複雑な条件(条件ごとのシート分割など)を実装したい場合は、ぜひ教えてください。次は「さらに一歩進んだアーキテクチャ」についてお話ししましょう。
あなたの自動化ライフが、より快適で創造的なものになりますように!
