Accessの深淵:DAO.QueryDefとADO.Commandの真実。適材適所の「極限」
Access開発において、多くのエンジニアが陥る罠がある。「とりあえずDAOを使えばいい」あるいは「ADOの方が新しいから優れている」という短絡的な思考だ。
しかし、真のアーキテクトは知っている。Accessというデータベースエンジンが、どのようなメモリ管理を行い、どのタイミングでクエリをコンパイルし、バックエンドとの通信をどう裁いているのかを。今回は、DAO.QueryDefとADO.Commandの境界線、その「最適解」を深掘りする。
—
1. DAO.QueryDef:Access内部処理の「支配者」
DAO(Data Access Objects)は、Accessのネイティブな心臓部であるJet/ACEエンジンと直結している。
なぜQueryDefなのか
QueryDefオブジェクトを保存しておく最大の利点は、「実行計画の事前コンパイル」にある。Accessは一度実行されたクエリの実行計画をキャッシュし、2回目以降の実行速度を劇的に向上させる。
特に、動的SQLを構築する際、コード内で文字列を連結して`DoCmd.RunSQL`や`CurrentDb.Execute`を叩くのは愚策だ。メモリ上に無駄なクエリ定義を量産し、デコンパイルの効率を下げ、データベースの肥大化を招く。
極限の知見:動的パラメータークエリの再利用
動的SQLを組む際は、一時的にQueryDefを作成し、`Parameters`コレクションを操作せよ。
‘ DAOによる動的SQLの最適実行例
Public Sub ExecuteOptimizedQuery(ByVal targetID As Long)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Set db = CurrentDb
‘ 既存のクエリ定義を流用、または一時的に生成
Set qdf = db.CreateQueryDef(“”, “SELECT FROM T_Orders WHERE OrderID = [pID]”)
‘ パラメーターをバインド(型定義を明示することでエンジンへの負荷を軽減)
qdf.Parameters(“pID”) = targetID
Dim rs As DAO.Recordset
Set rs = qdf.OpenRecordset(dbOpenSnapshot)
‘ — 処理 —
‘ オブジェクトの明示的解放(VBAのGCを待たない)
rs.Close: Set rs = Nothing
qdf.Close: Set qdf = Nothing
Set db = Nothing
End Sub
—
2. ADO.Command:外部DB連携の「特使」
一方で、SQL ServerやAzure SQL Databaseなどの外部DBをバックエンドに据えた場合、DAOはもはや足枷となる。ここでの最適解は、間違いなく ADO (ActiveX Data Objects) だ。
なぜADOなのか
DAOがACEエンジンを介して外部DBと通信する場合、一度「Jetエンジンによる翻訳」というオーバーヘッドが発生する。対してADOは、OLE DBプロバイダ(MSOLEDBSQLなど)を介して直接SQLを流し込む。
特に`ADODB.Command`オブジェクトを使用し、`Prepared = True`とすることで、SQL Server側での実行プランのキャッシュを最大限に活かせる。
極限の知見:接続プーリングとタイムアウト制御
外部連携で最も重要なのは「接続の枯渇」と「ブロック」だ。以下のパターンを標準装備せよ。
‘ ADOによる外部SQL Server連携の最適例
Public Sub ExecuteExternalCommand(ByVal sql As String, ByVal paramValue As String)
Dim conn As ADODB.Connection
Dim cmd As ADODB.Command
Set conn = New ADODB.Connection
conn.ConnectionString = “Provider=MSOLEDBSQL;Server=…;Database=…;Trusted_Connection=yes;”
conn.Open
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = conn
.CommandText = “UPDATE T_Log SET Status = ? WHERE LogID = ?”
.CommandType = adCmdText
.Prepared = True ‘ 実行計画をサーバー側でキャッシュさせる
‘ パラメーターの明示的指定(型不一致による暗黙の型変換を防ぐ)
.Parameters.Append .CreateParameter(“@Status”, adVarChar, adParamInput, 50, “Processed”)
.Parameters.Append .CreateParameter(“@ID”, adInteger, adParamInput, , paramValue)
.Execute
End With
conn.Close
Set cmd = Nothing: Set conn = Nothing
End Sub
—
3. シニアエンジニアが意識すべき「境界線」
現場のシステムを保守する際、以下のチェックリストを常に頭に置いてほしい。
1. 接続先はどこか?:
- `.accdb`内部 → `DAO` (QueryDefで定義し、実行計画を固定せよ)
- 外部RDBMS → `ADO` (Commandオブジェクトを使い、Preparedを意識せよ)
2. メモリ解放の義務:
- `Set Object = Nothing`は単なる儀式ではない。特に長時間稼働するAccessフロントエンドでは、参照カウントをゼロにしなければ、メモリリークが確実に発生する。
3. Windows APIとの共存:
- 大規模データ処理を行う際、`DoEvents`を乱用するな。代わりにWindows APIの`Sleep`や`MsgWaitForMultipleObjects`を用いて、CPUリソースと応答性のバランスを制御せよ。
結びに代えて
Accessは、もはや「初心者のための玩具」ではない。DAOとADOの特性を理解し、クエリのライフサイクルを制御できる者だけが、このレガシー環境で圧倒的なパフォーマンスを引き出すことができる。
コードは嘘をつかない。君が書いたその一行が、将来のデバッグ工数を決定づけることを忘れるな。明日の現場で、この知見が君の武器になることを期待する。
