【実務・中級編】マルチユーザー環境におけるQueryDefの競合問題と解決策 – Access VBA解析バイブル

スポンサーリンク

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` コレクションの型定義を叩き込む必要があるだろう。それはまた別の機会に話そう。

さあ、コードを書き換えろ。そして、バグを過去のものにせよ。

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