Access VBAの「QueryDef」という名の時限爆弾:マルチユーザー環境で死なないための設計論
Access開発者諸君。君たちは今、共有ネットワーク上のmdb/accdbで、`CurrentDb.QueryDefs(“MyQuery”).SQL = …` と書いて満足していないだろうか?
もし君のツールが「たまに謎の『書き込みロック』エラーが出る」あるいは「複数のユーザーが同時に検索ボタンを押すと、片方の条件で別のユーザーの検索結果が書き換わる」という現象に悩まされているなら、それは設計の敗北だ。
今回は、マルチユーザー環境でQueryDefを「破壊」せず、安全かつ高速に動的SQLを捌くためのアーキテクチャを伝授する。
—
1. なぜ「固定名」のQueryDefは罪深いのか
多くの初心者が犯す過ちは、`qdf_Search` といった固定名称のQueryDefオブジェクトを使い回すことだ。
1. 競合の発生: ユーザーAがSQLを書き換えた直後、ユーザーBがSQLを上書きする。ユーザーAが実行する直前に中身が入れ替わり、予期せぬデータが表示される(あるいは書き込みロックで落ちる)。
2. 実行計画の汚染: 共有データベースにおいてQueryDefを直接書き換える行為は、Accessの内部的なカタログ(MSysObjects)への書き込みを強制する。これはネットワーク負荷を高め、ファイル破損のトリガーとなる。
結論:QueryDefは「書き換えるもの」ではなく「使い捨てのパーツ」として扱うべきだ。
—
2. 解決策:ランダム・クエリ名生成戦略
最も堅牢なアプローチは、「実行のたびに一時的な名前のQueryDefを生成し、使い終わったら即座に破棄する」ことだ。
実装の極意
- GUID/乱数の活用: `VBA.CreateObject(“Scriptlet.TypeLib”).GUID` や乱数を使用して、絶対に衝突しない一時的なクエリ名を生成する。
- 例外処理によるクリーンアップ: どんなエラーが起きても、生成したQueryDefは必ず `Delete` して掃き出す。
—
3. 実践:プロダクション・レベルの動的SQL実行コード
このコードは、マルチユーザー環境でも絶対に干渉しない「安全地帯」を作るためのテンプレートだ。
‘ —————————————————————————
‘ 概要:マルチユーザー環境で競合を回避する動的クエリ実行モジュール
‘ —————————————————————————
Public Sub ExecuteDynamicQuery(ByVal strSQL As String)
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim tmpName As String
‘ GUIDからハイフンを除去した一意な名前を生成
tmpName = “tmp_” & Replace(Mid(CreateObject(“Scriptlet.TypeLib”).GUID, 2, 36), “-“, “”)
Set db = CurrentDb
On Error GoTo Cleanup
‘ 一時的なQueryDefを作成
Set qdf = db.CreateQueryDef(tmpName, strSQL)
‘ フォームのレコードソースに割り当てて表示(またはDoCmd.OpenQuery)
‘ 例: Forms!frmMain.RecordSource = tmpName
‘ 必要に応じてここでOpenRecordsetなどを行う
‘ Dim rs As DAO.Recordset
‘ Set rs = qdf.OpenRecordset
Cleanup:
‘ どんな状況でも必ず削除してリソースを解放する
If Not qdf Is Nothing Then
db.QueryDefs.Delete tmpName
Set qdf = Nothing
End If
If Err.Number <> 0 Then
MsgBox “クエリ実行エラー: ” & Err.Description, vbCritical
End If
End Sub
—
4. プロの視点:このコードが優れている理由
1. ステートレスな設計: 関数が終了するたびにクエリ定義が消滅するため、データベース内にゴミが残らない。ゴミが残らないということは、MSysObjectsの肥大化を防ぎ、パフォーマンスを維持できるということだ。
2. 排他制御からの解放: データベース全体をロックするような構造を排除しているため、同時に10人が同じツールを叩いてもエラーは発生しない。
3. 保守性の担保: 複雑なクエリ生成ロジックをクラスや別モジュールに切り出せば、SQL生成部分と実行部分を完全に分離できる。
—
最後に:エンジニアとしての矜持
「動けばいい」というコードは、誰でも書ける。だが、「誰がどう使っても壊れない」というコードは、設計者の哲学が宿る。
Accessは古いプラットフォームだと揶揄されることもあるが、その内部動作(DAOの挙動、ロックファイル、トランザクション管理)を理解すれば、驚くほど堅牢な業務システムを構築できる。
君たちのコードが、次なる「伝説」になることを期待している。
もし、この実装をさらに拡張したい(例:パラメータークエリへの対応など)という野望があるなら、次は `Parameters` コレクションの型定義を叩き込む必要があるだろう。それはまた別の機会に話そう。
さあ、コードを書き換えろ。そして、バグを過去のものにせよ。
