【テクニカル・上級編】【上級】スキーマ変更の履歴を自動記録する「テーブル定義変更ログ」の実装 – Access VBA解析バイブル

スポンサーリンク

Accessの深淵を覗く:スキーマ変更を「検知」するアーキテクチャの真実

Accessというプラットフォームは、古くから「誰でも触れる」という免罪符のもと、制御不能な混沌(カオス)を生み出してきた。特に、テーブル定義の変更履歴が残らないという仕様は、大規模開発において致命的な「証跡の欠如」を招く。

今日語るのは、DAO(Data Access Objects)の深層を突き、`TableDef`の変更をリアルタイムで監視・記録する「スキーマ・監査基盤」の構築手法だ。付け焼き刃の運用ルールではなく、システムそのものに自律的な監視機能を持たせる。

1. 概念の転換:イベント駆動の限界と「スキーマ・スナップショット」

Accessの標準機能には、テーブル定義変更を捕捉する「DBイベント」は存在しない。ここで多くのエンジニアは諦めるが、真のアーキテクトは「状態の差分」に注目する。

スキーマ変更の記録には、`DAO.TableDef`オブジェクトを再帰的に走査し、ハッシュ値あるいはメタデータ構造をバックアップテーブルと比較する手法を採用する。これをアプリケーション起動時、あるいは管理メニューの裏側で強制実行させるのが最も堅牢だ。

2. 実装:スキーマ変更検知エンジンのコアロジック

以下のコードは、テーブルのフィールド構成をシリアライズし、変更差分を特定するためのエンジンの断片である。

‘ スキーマ変更監査エンジン
‘ 依存関係: DAO (Microsoft Office x.x Access database engine Object Library)

Public Sub CaptureSchemaSnapshot()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim rsLog As DAO.Recordset

Set db = CurrentDb
Set rsLog = db.OpenRecordset(“tbl_SchemaAuditLog”, dbOpenDynaset)

On Error GoTo Cleanup

‘ システムテーブルを除外してイテレーション
For Each tdf In db.TableDefs
If Left(tdf.Name, 4) <> “MSys” Then
For Each fld In tdf.Fields
‘ ここでプロパティ(Type, Size, Attributes)をハッシュ化し、
‘ 直近のログと比較する。
‘ 実務ではここに Windows API で取得したユーザー名を付与する
With rsLog
.AddNew
!TableName = tdf.Name
!FieldName = fld.Name
!FieldType = fld.Type
!ChangeDate = Now()
!ChangedBy = GetWindowsUser() ‘ API経由で取得
.Update
End With
Next fld
End If
Next tdf

Cleanup:
‘ オブジェクトの明示的解放はVBAにおける「礼儀」ではない、必須の防衛策である
If Not rsLog Is Nothing Then rsLog.Close: Set rsLog = Nothing
Set tdf = Nothing
Set db = Nothing
End Sub

3. Windows APIによる「操作主」の特定

`Environ(“Username”)`を信頼してはならない。それはOSの環境変数を書き換えるだけで容易に偽装できる。監査ログにおいて最も重要なのは「誰が」というエビデンスだ。

If VBA7 Then
Private Declare PtrSafe Function GetUserName Lib “advapi32.dll” Alias “GetUserNameA” _
(ByVal lpBuffer As String, nSize As Long) As Long
Else
Private Declare Function GetUserName Lib “advapi32.dll” Alias “GetUserNameA” _
(ByVal lpBuffer As String, nSize As Long) As Long
End If

Public Function GetWindowsUser() As String
Dim strBuffer As String 255
Dim lngSize As Long
lngSize = 255
If GetUserName(strBuffer, lngSize) > 0 Then
GetWindowsUser = Left(strBuffer, InStr(strBuffer, vbNullChar) – 1)
End If
End Function

4. アーキテクトの視点:パフォーマンスとメモリの最適化

  • オブジェクトの解放: `Set obj = Nothing`を怠ることは、Accessのメモリリークを放置することと同義だ。特にバックエンドが共有フォルダ上にある場合、解放漏れはレコードロックの解放遅延を引き起こし、致命的な競合を誘発する。
  • バックエンドの分離: 監査ログテーブルは、フロントエンドではなく、必ずリンクされたバックエンド(SQL Serverや別mdb)に配置すること。フロントエンドにログを保持すると、開発者がフロントエンドを上書き配布した瞬間に全履歴が消滅する。
  • 非同期実行の検討: スキーマ解析はコストが高い。起動時に毎回走査するのではなく、専用の「管理用フォーム」が開かれた時、あるいはバックグラウンドでタイマーイベントを使い、ユーザーの操作を阻害しないタイミングで実行させるのがプロの仕事だ。

結論:Accessを「管理不能」から「追跡可能」なシステムへ

Accessは、設計次第で極めて強力な管理ツールに化ける。しかし、それは「ブラックボックス」を許容しない者だけに与えられる権利だ。

スキーマ変更を記録するということは、システムの「進化の過程」を記録することと同義である。コードをただ動かすフェーズから、システムの状態を常に可視化し、説明責任(アカウンタビリティ)を果たすフェーズへ移行せよ。

それが、レガシーを「遺産」に変えるための唯一の道だ。

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