Access VBAにおける「完全同期エンジン」の構築:DAOとADOXの混成によるメタデータ制御の極致
Access開発において、フロントエンドの改修は容易でも、運用中のバックエンド(BE)のテーブル定義変更は常に「恐怖」を伴う。ユーザーが接続している最中にBEを差し替えることは許されず、かといって手動のALTER TABLEを繰り返せば、いずれ整合性の崩壊(=システム死)を招く。
本稿では、開発用DB(マスター)と運用用DB(ターゲット)の差異をメタデータレベルで検出し、必要なパッチのみを原子的に適用する「完全同期エンジン」の設計思想を解説する。
—
1. なぜDAOとADOXを「使い分ける」のか
多くの初学者はADOXのみで完結させようとするが、それは誤りだ。
- DAO (`DAO.TableDef`): Accessのネイティブな挙動に最適化されており、プロパティの制御やインデックスの微細な操作に長けている。しかし、プロバイダ非依存の拡張性には乏しい。
- ADOX (`ADOX.Catalog`): SQL標準に近く、データ型の変換や外部DB接続の抽象化に優れる。
結論: 「テーブル定義の構造解析にはDAOを用い、変更の適用(特にDDL発行や型変換)にはADOXを併用する」のが、Accessの深淵を知る者の最適解である。
—
2. 「完全同期エンジン」の設計指針
同期エンジンを構築する際、決して「全削除して作り直す」という愚を犯してはならない。データ損失は致命的だ。以下のフェーズで実装する。
1. メタデータ・スナップショット: 両DBの`TableDefs`を走査し、Dictionary型にフィールド・インデックスのハッシュ値を保持する。
2. 差分抽出アルゴリズム: 名前の不一致、型の不一致、必須制約の欠落を検出し、優先順位(追加 > 変更 > 削除)に従ってキューを作成する。
3. トランザクション管理: `DBEngine.BeginTrans`を用いて、万が一の失敗時にロールバックを強制する。
—
3. 実装コード:差分適用エンジンのコアロジック
以下は、フィールドの追加を例とした、メモリリークを許さないクリーンな実装の断片である。
‘ 参照設定: Microsoft DAO 3.6 / Microsoft ADO Ext. 2.8 for DDL and Security
Public Sub SyncTableField(ByVal strTargetDB As String, ByVal tblName As String, ByVal fldName As String, ByVal fldType As Integer)
Dim cat As ADOX.Catalog
Dim tbl As ADOX.Table
Dim col As ADOX.Column
Dim conn As ADODB.Connection
‘ メモリリークを防ぐための明示的クリーンアップ戦略
On Error GoTo Cleanup
Set conn = New ADODB.Connection
conn.Open “Provider=Microsoft.ACE.OLEDB.12.0;Data Source=” & strTargetDB
Set cat = New ADOX.Catalog
Set cat.ActiveConnection = conn
Set tbl = cat.Tables(tblName)
‘ フィールド存在確認
If Not FieldExists(tbl, fldName) Then
Set col = New ADOX.Column
With col
.Name = fldName
.Type = fldType
‘ 必要に応じて .DefinedSize を指定
End With
tbl.Columns.Append col
Debug.Print “Success: Field ” & fldName & ” appended to ” & tblName
End If
Cleanup:
‘ オブジェクトの明示的解放(VBAのGCを待たない)
If Not col Is Nothing Then Set col = Nothing
If Not tbl Is Nothing Then Set tbl = Nothing
If Not cat Is Nothing Then Set cat.ActiveConnection = Nothing: Set cat = Nothing
If Not conn Is Nothing Then conn.Close: Set conn = Nothing
If Err.Number <> 0 Then Err.Raise Err.Number, , “Sync Error: ” & Err.Description
End Sub
—
4. 極限の知見:レガシー保守の要諦
メモリの墓場を避ける
VBAのオブジェクトは、スコープを抜けても即座にメモリから解放されるとは限らない。特にADO系のオブジェクトをループ内で生成・破棄する場合、`Set = Nothing`を徹底し、さらに必要であれば `DoEvents` を挟むことで、バックグラウンドでのメモリ解放を促せ。
Windows APIによる排他制御の確認
同期処理を開始する前に、対象のBEファイルが他者にロックされていないかを確認する必要がある。
`CreateFile` APIを用い、`GENERIC_WRITE`権限でファイルを開こうとすることで、即座にロック状態を検知できる。これは`Dir()`関数よりも遥かに低レイヤーで確実な手法だ。
システム間連携とデータ型の落とし穴
Accessの`Long Integer`とSQL Serverの`INT`が微妙に挙動を異にするケースがある。定義同期の際は、必ず「標準SQL型へのマッピングテーブル」を別途保持し、型変換関数を通すこと。これを怠ると、将来的にクエリのインデックスが効かなくなる致命的なパフォーマンス劣化を招く。
—
最後に
Accessは単なる「デスクトップのオモチャ」ではない。適切に設計された同期エンジンを搭載すれば、それは堅牢な企業向けミドルウェアへと昇華する。
コードを書くとき、常に考えろ。「もし今日、このシステムが100人の同時接続を受けたら、このオブジェクトはメモリを食い尽くさないか?」と。
その問いこそが、真のエンジニアと単なる実装者を分かつ境界線である。
