【入門編】大量データ処理を高速化!QueryDefのキャッシュと再利用戦略 – Access VBA解析バイブル

スポンサーリンク

こんにちは!Access VBAでの開発、日々の業務お疲れ様です。
「マクロの記録」ボタンを押すだけの世界から一歩踏み出し、自分の手でコードを書き始めたあなたへ。ここをクリアすれば、Access VBAの基本はバッチリですよ、という最高にエキサイティングなテーマをお届けします。

今日フォーカスするのは「QueryDef(クエリディフィニション)のキャッシュと再利用」です。
「なんだか難しそうな名前だな…」と思いましたか? 大丈夫、優しく噛み砕いて本質をお伝えしますね。ここを押さえるだけで、あなたの書くプログラムは見違えるほど高速になり、まわりのエンジニアをアッと言わせることができるようになりますよ。

—

1. なぜ「毎回SQL文を書く」と遅いのか?

例えば、画面で入力された条件に合わせて、大量のデータ(数千件〜数万件)を1件ずつテーブルに登録・更新する処理を作るとします。

初学者がやりがちなのが、こんなコードです。

‘ 【NGパターン】ループの中で毎回SQLを組み立てて実行する
Dim i As Long
For i = 1 to 10000
‘ 毎回SQL文字列を生成
Dim sql As String
sql = “INSERT INTO T_売上 (顧客ID, 金額) VALUES (” & Me.txtID & “, ” & Me.txtAmmount & “);”

‘ 実行!
CurrentDb.Execute sql, dbFailOnError
Next i

このコード、実はAccess(データベースエンジン)にとってものすごく負担が大きいんです。
なぜなら、データベースはSQL文を受け取るたびに、「この文字列はどういう意味だ?どのテーブルのどのインデックスを使えば速いんだ?」と、頭の中でパース(解析)し、実行計画を立てる作業を毎回合計1万回もやっているからです。

人間で例えるなら、料理の注文を1品受けるたびに、料理長が一からレシピの全工程を考え直しているようなもの。これでは時間がかかって当然ですよね。

—

2. 救世主「QueryDef」とプリコンパイルの魔法

ここで登場するのが、今回の主役である QueryDef(クエリ定義オブジェクト) です。

QueryDefをあらかじめデータベースに登録しておく(あるいはメモリ上に保持する)と、データベースエンジンは「最初に1回だけSQLの解析と実行計画の作成(プリコンパイル)」を行い、その結果をキャッシュ(記憶)してくれます。

2回目以降は、解析済みのエンジンに対して「パラメータ(条件値)だけを渡して実行!」とするだけで済むため、処理スピードが劇的に跳ね上がるのです。

イメージ図にしてみましょう。

【通常(都度パース)】
SQL文字列生成 ➔ 【解析・計画(重い!)】 ➔ 実行 ➔ (ループで繰り返し…)

【QueryDefのキャッシュ戦略】
[事前準備] QueryDef作成 ➔ 【解析・計画(最初の一回だけ!)】
[ループ内] パラメータ代入 ➔ 実行高速! ➔ パラメータ代入 ➔ 実行高速!

—

3. 実践!QueryDefをキャッシュして高速化するコード

それでは、現場でそのまま使える実用的なコードを見ていきましょう。
今回は、一時的なQueryDefをメモリ上に作成し、パラメータ(Parameters)を差し替えながら高速にデータを登録するパターンを解説します。

Public Sub HighSpeedInsertSample()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim i As Long
Dim startTime As Double

startTime = Timer ‘ 処理時間計測用

Set db = CurrentDb

On Error GoTo ErrorHandler

‘ 1. あらかじめ「パラメータ付きSQL」の骨組みをQueryDefとして定義する
‘ パラメータには「?」または名前付き(今回は分かりやすく名前付き)を使用します
Dim sqlText As String
sqlText = “PARAMETERS prmCustomerID Long, prmAmount Currency; ” & _
“INSERT INTO T_売上 (顧客ID, 金額, 登録日時) ” & _
“VALUES (prmCustomerID, prmAmount, Now());”

‘ 一時的なQueryDefとしてデータベースに登録(名前を空にすると一時オブジェクトになる)
Set qdf = db.CreateQueryDef(“”, sqlText)

‘ 2. ループ処理:SQLの解析は終わっているので、パラメータを入れ替えて実行するだけ!
For i = 1 to 5000
‘ パラメータに値をバインド(設定)する
qdf.Parameters(“prmCustomerID”) = i
qdf.Parameters(“prmAmount”) = i 150 ‘ ダミーの金額

‘ 実行(dbFailOnErrorを付けることでエラー時にロールバックできるようにする)
qdf.Execute dbFailOnError
Next i

MsgBox “処理が完了しました! 実行時間: ” & Format(Timer – startTime, “0.00”) & “秒”, vbInformation

CleanUp:
‘ 3. 使い終わったQueryDefは必ずメモリから解放する
If Not qdf Is Nothing Then qdf.Close
Set qdf = Nothing
Set db = Nothing
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub

コードのここがポイント!

  • `db.CreateQueryDef(“”, sqlText)`: 名前を空文字(`””`)にすることで、Accessのナビゲーションウィンドウ(左側の画面)を汚さず、メモリ上だけにQueryDef(一時クエリ)を作成できます。
  • `PARAMETERS` 句の明示: SQLの先頭でデータ型をしっかり宣言するのがプロの技。Accessが「あ、この変数はLong型だな」と迷わず最適化できます。
  • `qdf.Execute dbFailOnError`: パラメータクエリの実行には `.Execute` メソッドを使います。

—

4. 陥りやすい罠とエラー回避の知見

実務でこのテクニックを使う際、初学者がハマりがちなポイントをいくつかシェアしておきますね。

① パラメータ名の大文字・小文字やスペルミス

`qdf.Parameters(“prmCustomerID”)` と指定した名前が、SQL文の中の `PARAMETERS prmCustomerID` と1文字でも違っていると、実行時に「パラメータが見つかりません(実行時エラー 3061)」というエラーが発生します。コピペミスに気をつけましょう。

② 明示的なクローズ(Close)とメモリ管理

`CreateQueryDef` で作成したオブジェクトは、VBAの処理が終わるまでメモリに居座り続けます。必ず `qdf.Close` と `Set qdf = Nothing` をセットで行い、メモリリーク(メモリの無駄遣い)を防ぎましょう。サンプルコードのように `On Error GoTo` を使って、エラー時でも確実に掃除(CleanUp)される仕組みにしておくのがエンジニアの作法です。

—

まとめ

いかがでしたでしょうか?
「毎回SQLを組み立ててパースさせる」やり方から、「QueryDefを構築してパラメータだけを流し込む」やり方へシフトするだけで、Access VBAのパフォーマンスは見違えるほど向上します。特に数千件以上のレコードを扱うバッチ処理やインポート処理では、この差が数秒から数分、あるいは「フリーズしたかと思うほどの遅さ」との分水嶺になります。

ここをクリアしたあなたは、もうただの「マクロ初心者」ではありません。データベースの挙動を意識できる、ワンランク上の「Access VBAエンジニア」への第一歩を踏み出しました。

ぜひ、実際の開発現場のループ処理で試してみてくださいね。あなたのプログラミングライフを、これからも応援しています!

タイトルとURLをコピーしました