Accessの「魔境」を可視化せよ:QueryDefパフォーマンスプロファイラの極意
Access開発の現場で、ある日突然システムが重くなる。原因を探ると、数十個あるクエリのどれかがボトルネックになっている……。この「犯人探し」に貴重な時間を浪費してはいないだろうか。
多くの開発者が、クエリの実行速度を「なんとなく」の感覚で判断している。しかし、真のプロフェッショナルは計測する。Accessの心臓部である `QueryDef` を掌握し、実行時間を自動でトレースする仕組みを構築すれば、パフォーマンス問題は「運」ではなく「管理可能な数値」へと変わる。
本稿では、全てのクエリ実行を監視・記録し、遅延クエリを自動で炙り出す「QueryDefパフォーマンスプロファイラ」の設計思想と実装を伝授する。
—
1. なぜ「QueryDef」を直接叩く必要があるのか
初心者は `DoCmd.RunSQL` を多用するが、これはデバッグが困難で、実行プランのキャッシュも効きにくい。対して `QueryDef` オブジェクトは、Accessのクエリエンジンが解析した実行プランを保持できるため、適切に使えば爆速になる。
しかし、複雑なJOINや相関サブクエリが混ざると、途端に性能が劣化する。我々が作るべきは、「クエリの実行開始と終了をラップし、その差分を記録するインターセプター(監視層)」である。
2. アーキテクチャ設計:計測の罠を避ける
計測ロジックを実装する際、最も注意すべきは「計測そのものがオーバーヘッドにならないこと」だ。
- 高精度タイマーの使用: `Timer` 関数では精度が足りない。Windows APIの `QueryPerformanceCounter` を使用せよ。
- ログの分離: パフォーマンスログを本番のテーブルに直接書き込むと、排他制御でシステムが停止する。必ず別ファイル(ログ専用MDB)またはデバッグウィンドウへの非同期出力を行うこと。
- 堅牢性: 計測エラーが発生しても、本来の業務処理を止めないよう `On Error Resume Next` を戦略的に配置する。
—
3. 実装:QueryDefパフォーマンスプロファイラ
以下のコードは、既存の `QueryDef` をラップして実行するための汎用関数だ。これを共通モジュール(`modQueryEngine`)に配置する。
Option Compare Database
Option Explicit
‘ 高精度タイマー用API
If VBA7 Then
Private Declare PtrSafe Function QueryPerformanceCounter Lib “kernel32” (lpPerformanceCount As Currency) As Long
Private Declare PtrSafe Function QueryPerformanceFrequency Lib “kernel32” (lpFrequency As Currency) As Long
Else
Private Declare Function QueryPerformanceCounter Lib “kernel32” (lpPerformanceCount As Currency) As Long
Private Declare Function QueryPerformanceFrequency Lib “kernel32” (lpFrequency As Currency) As Long
End If
‘ 閾値(ミリ秒)
Private Const SLOW_QUERY_THRESHOLD As Double = 500
Public Sub ExecuteQueryMeasured(queryName As String, ParamArray params() As Variant)
Dim qdf As DAO.QueryDef
Dim freq As Currency, startCount As Currency, endCount As Currency
Dim elapsedTime As Double
Dim i As Integer
Set qdf = CurrentDb.QueryDefs(queryName)
‘ パラメータの注入
For i = 0 To UBound(params)
qdf.Parameters(i) = params(i)
Next i
‘ 計測開始
QueryPerformanceFrequency freq
QueryPerformanceCounter startCount
‘ 実行
qdf.Execute dbFailOnError
‘ 計測終了
QueryPerformanceCounter endCount
elapsedTime = (endCount – startCount) / freq 1000 ‘ ミリ秒換算
‘ 閾値超えのクエリをイミディエイトウィンドウに警告
If elapsedTime > SLOW_QUERY_THRESHOLD Then
Debug.Print “【PERFORMANCE ALERT】” & queryName & “: ” & Format(elapsedTime, “0.00”) & “ms”
‘ ここでログテーブルへの書き込み処理を呼び出す
End If
Set qdf = Nothing
End Sub
—
4. 運用のための「賢い」ヒント
このコードを導入しただけでは不十分だ。以下のルールをチーム全体で徹底してほしい。
1. クエリ名の命名規則: `qry_` プレフィックスを強制し、どのクエリが実行されているかスタックトレースで分かるようにする。
2. パラメータクエリの活用: 文字列連結でSQLを生成するのは論外だ。SQLインジェクションのリスクに加え、クエリプランの再利用が阻害される。必ず `QueryDef.Parameters` を通すこと。
3. ログの集計: ログテーブルに溜まったデータは、週に一度は「平均実行時間」と「実行回数」でソートし、改善すべきクエリを特定する会議を開くこと。これがエンジニアリングの「文化」を作る。
結びに:計測なき最適化は、ただの勘である
システムが遅いとき、闇雲にインデックスを貼るエンジニアは二流だ。まず計測し、どのSQLが、どのデータセットで、何ミリ秒かかっているかを数値化せよ。
Accessは適切に扱えば、驚くほど堅牢な業務基盤になる。今日からこのプロファイラを導入し、あなたのシステムを「ブラックボックス」から「透明性の高いエンジニアリングの結晶」へと昇華させてほしい。
技術は裏切らない。計測した分だけ、システムは必ず最適化される。
