Access VBAで「画面フリーズ」を撲滅せよ!QueryDefと非同期CSV出力の極意
こんにちは。Access開発の現場で、一度は必ずぶつかる壁があります。「数万件のデータを抽出した瞬間、Accessがフリーズして応答なしになる」。
初心者の方が陥りやすいこの「魔の瞬間」を回避し、プロフェッショナルな挙動を実現する「非同期CSV出力」のテクニックを伝授します。単に動くコードを書くのではなく、「OSとアプリケーションの対話」を理解する、一歩先の世界へ行きましょう。
—
なぜ「フリーズ」が起きるのか?
Accessでクエリを実行すると、Accessは「全レコードをメモリ上に読み込み、画面のグリッドに描画する」という重い作業を一気に行います。これがCPUとメモリを占有し、OSが「Accessは死んだ」と誤認してフリーズ状態を誘発するのです。
私たちが目指すのは、「クエリの実行」と「ファイルへの書き出し」を小分けにし、その合間にOSに呼吸(休憩)させる処理です。
—
鍵となる技術:QueryDefとDoEvents
ここで登場するのが`QueryDef`と`DoEvents`です。
- QueryDef: クエリをオブジェクトとして操作し、パラメータを柔軟に注入します。
- DoEvents: これが魔法の関数です。実行中のVBAを一時停止させ、OSに「いま入力操作や画面更新のイベントを受け付けていいよ」と許可を与えます。
—
実践コード:非同期CSV出力の設計
以下のコードは、バックグラウンドでクエリを走らせながら、プログレスバーのように状況を更新しつつCSVを書き出す仕組みです。
Public Sub ExportQueryToCsvAsync(queryName As String, filePath As String)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
Dim fileNum As Integer
Dim i As Long
Set db = CurrentDb
Set qdf = db.QueryDefs(queryName)
‘ パラメータがある場合はここで値を設定
‘ qdf.Parameters(“[開始日]”) = #2023/01/01#
‘ スナップショット形式で読み込む(高速化の鉄則)
Set rs = qdf.OpenRecordset(dbOpenSnapshot)
fileNum = FreeFile
Open filePath For Output As #fileNum
‘ レコードが空なら終了
If rs.EOF Then GoTo Cleanup
‘ 処理のループ
Do While Not rs.EOF
‘ 1行ずつCSV形式に変換して書き出し
Print #fileNum, rs!ID & “,” & rs!DataName & “,” & rs!Amount
rs.MoveNext
‘ 【重要】100件ごとに画面の更新権をOSに返却する
‘ これによりフリーズを防ぎます
If rs.AbsolutePosition Mod 100 = 0 Then
DoEvents
Debug.Print “処理中…” & rs.AbsolutePosition
End If
Loop
Cleanup:
Close #fileNum
rs.Close
Set rs = Nothing
Set qdf = Nothing
MsgBox “CSV出力が完了しました!”, vbInformation
End Sub
—
コードのポイント:ここを理解すればマスター!
1. `dbOpenSnapshot` の魔法
`OpenRecordset` を使う際、デフォルトではなくあえて `dbOpenSnapshot` を指定してください。これは「読み取り専用・メモリ内コピー」を意味し、データ編集のオーバーヘッドを省くため、純粋な抽出速度が段違いに速くなります。
2. `DoEvents` の立ち位置
`DoEvents` は強力ですが、多用しすぎると処理速度が落ちます。上記のコードのように `Mod 100 = 0`(100件ごと)といった具合に、適度なインターバルを設けるのが「プロの加減」です。
3. `FreeFile` の重要性
`Open #1` のように番号を決め打ちするのは危険です。他の処理がその番号を使っている可能性があるため、`FreeFile` 関数を使ってOSから空いているファイル番号を借りるのが、堅牢なコードの第一歩です。
—
陥りやすいエラーと対策
- 「クエリが見つかりません」エラー: `QueryDefs` は保存済みのクエリしか参照できません。動的SQLを生成する場合は、一時的に `CREATE QUERYDEF` で作成するか、SQL文を直接 `db.OpenRecordset` に渡す手法を検討しましょう。
- メモリ解放の忘れ: `Set rs = Nothing` を省略すると、Accessは「まだ何か作業があるかも?」とメモリを抱え込みます。大規模データほど、この解放処理が命取りになります。
—
最後に:先輩エンジニアからのエール
Access VBAは古いと言われますが、「データとユーザーの橋渡し」をこれほど高速に、かつ柔軟に実装できるツールは他にありません。
今回紹介した「画面を止めない」という工夫は、ユーザーからの「Accessって重いよね」という不満を「これなら使いやすい!」という信頼に変える大きな一歩です。まずはこのコードをコピーして、あなたのプロジェクトの重い処理に組み込んでみてください。
ここをクリアすれば、あなたはもうマクロの記録を卒業した「エンジニア」です。応援していますよ!
