Access VBAの深淵へ:クエリが「遅くなる」理由と、実行計画を強制刷新する技術
こんにちは。現場で日々、泥臭い自動化と格闘しているエンジニアの皆さん。
Accessで開発をしていると、ある日突然「昨日まで爆速だったクエリが、なぜか今日から激重になった」という怪奇現象に遭遇しませんか?
再起動しても直らない。再コンパイルしてもダメ。これ、実はAccessの「実行計画」が古いまま固定されていることが原因かもしれません。今回は、QueryDef(クエリ定義)と動的SQLの裏側にある「統計情報の罠」と、それをVBAで掌握する方法についてお話しします。
—
1. なぜクエリは「老いる」のか?
Accessのデータベースエンジン(ACE)は、クエリを実行する際、そのSQLをどう処理するのが一番速いかという「実行計画(Execution Plan)」を立てます。
しかし、テーブルのデータが数万件、数十万件と増減していくうちに、「昔の計画」が「今のデータ量」と合わなくなることがあります。これが「クエリの老化」です。
本来、ACEは賢いので自動で再最適化を試みますが、VBAで `QueryDef` を多用していると、この最適化がうまく走らず、負の連鎖に陥ることがあります。
—
2. QueryDefを「強制リフレッシュ」するVBAテクニック
QueryDefは、保存されたクエリをオブジェクトとして扱う強力な武器です。しかし、中身のSQLを書き換えて実行する際、ただ `SQLプロパティ` を上書きするだけでは不十分なケースがあります。
以下のコードは、QueryDefを一度「削除して作り直す」という、最も荒っぽく、しかし最も確実な「実行計画の強制リフレッシュ術」です。
Public Sub RefreshQueryDef(strQueryName As String, strSQL As String)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Set db = CurrentDb
‘ 1. 既存のクエリが存在するか確認
On Error Resume Next
Set qdf = db.QueryDefs(strQueryName)
‘ 2. 存在する場合は一度削除して「記憶」をリセットする
If Not qdf Is Nothing Then
db.QueryDefs.Delete strQueryName
End If
On Error GoTo 0
‘ 3. 新しく定義し直すことで、ACEに「これは新しいクエリだ」と認識させる
‘ これにより、実行計画がゼロベースで再生成される
Set qdf = db.CreateQueryDef(strQueryName, strSQL)
‘ 4. 後片付け
qdf.Close
Set qdf = Nothing
Set db = Nothing
Debug.Print “クエリ [” & strQueryName & “] の再構築が完了しました。”
End Sub
なぜこれで速くなるのか?
`Delete` して `Create` することで、ACEの内部キャッシュに溜まった古い実行プランを強制的に破棄させています。いわば、エンジンの「キャッシュクリア」ですね。
—
3. パラメータークエリで「静的なSQL」を避ける
初学者が陥りやすいのが、「SQL文の中に値を直接埋め込んでしまう(連結する)」手法です。
‘ 悪い例:これだと毎回SQLが別物と判定され、キャッシュが効かない
strSQL = “SELECT FROM T_売上 WHERE 客ID = ” & Me.txtID
これだと、値が変わるたびに「全く新しいSQL」として認識され、毎回実行計画の生成コストがかかります。「パラメータークエリ」を使いましょう。
‘ 良い例:SQLの形は固定し、値だけを渡す
Dim qdf As DAO.QueryDef
Set qdf = CurrentDb.QueryDefs(“q_売上検索”)
qdf.Parameters(“p_客ID”).Value = Me.txtID
‘ これならSQL文の解析コストが最小化される
Dim rs As DAO.Recordset
Set rs = qdf.OpenRecordset()
—
4. 現場で役立つ「統計情報」の心得
ここからが、中級者へステップアップするための本質です。
1. コンパクト修復を定期的に: Accessの「データベースのコンパクトと修復」は、単なる容量削減ではありません。内部のインデックス統計情報を再計算させる重要な儀式です。
2. インデックスの貼る場所を疑う: クエリが遅い原因の9割は、WHERE句やJOIN句のフィールドにインデックスがないことです。どんなにコードを最適化しても、フルスキャンには勝てません。
3. 複雑すぎるクエリを分割する: 一つのクエリで何もかも解決しようとせず、一時テーブル(TempTable)を活用しましょう。中間結果を物理的に保持することで、ACEの負担は劇的に減ります。
—
最後に:エンジニアとしての心構え
「コードを書いて終わり」ではなく、「そのコードがデータベースエンジンとどう対話しているか」を想像すること。これができるだけで、あなたの書くVBAの品質は劇的に向上します。
最初は難しく感じるかもしれませんが、まずは「クエリを削除して作り直す」という小さな一歩から試してみてください。Accessが驚くほど軽やかに動き出す瞬間、きっとエンジニアとしての醍醐味を感じられるはずです。
ここをクリアすれば、あなたはもうただの「マクロ使い」ではありません。Accessの神髄に触れた、一人前のアーキテクトです。応援していますよ!
