【Access VBAを掌握する極限の知見】DB内に自己完結型ドキュメントを構築する:TableDefの「説明」プロパティをVBAで完全制御する
レガシーなAccessデータベースの保守において、最も絶望的な瞬間は何か。それは、数年前に退職した前任者が残した「仕様書の影すらない、魔改造された巨大なmdb/accdb」と対峙したときだ。外部のExcel定義書を探す旅に出る必要などない。答えは常に、Accessの内部構造(System Catalog)に埋め込むべきなのだ。
今回は、DAO(Data Access Objects)の深層を突く。テーブルの「説明(Description)」プロパティをVBAで自在に操り、データベース自体が自らの仕様書を語る「自己完結型アーキテクチャ」の構築法を伝授する。
—
1. なぜ「説明」プロパティのVBA制御なのか?
GUI(Accessの画面上)からテーブルを右クリックし、「オブジェクトのプロパティ」を開いて手動で説明文を入力する……そんな非効率な作業をシニアエンジニアがやるべきではない。
数百あるテーブルやフィールドに対し、一括でメタデータを流し込む、あるいはバージョン管理システム(VCS)や外部設計書DBからAPI的に同期を取る。これができるのはVBAによるプログラム制御だけだ。
しかし、Access VBAにおけるプロパティ操作には、知る人ぞ知る「トラップ」が存在する。存在しないプロパティへ直接アクセスした際に発生する「実行番号3270(プロパティが見つかりません)」の嵐である。ここをいかにエレガントに、かつ堅牢に突破するか。そこにエンジニアの技量が問われる。
—
2. 【極限のコード】動的プロパティ生成とメモリ管理の鉄則
DAOの`TableDef`や`Field`オブジェクトにおける`Description`プロパティは、最初からすべてのオブジェクトに実体として存在するわけではない。一度も値が設定されていない場合、そのプロパティはコレクションに存在しない「動的プロパティ」として扱われる。
以下の実用コードを見てほしい。エラーハンドリング、存在判定、そしてCOMコンテキストの解放(メモリ最適化)まで考慮した、プロダクション品質のルーチンだ。
Option Compare Database
Option Explicit
‘ =========================================================================
‘ 処理名 : SetTableAndFieldDescriptions
‘ 概要 : 指定したテーブルおよびフィールドの「説明」プロパティを設定する
‘ 備考 : プロパティが存在しない場合は動的に生成(CreateProperty)する
‘ =========================================================================
Public Sub SetTableAndFieldDescriptions()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
On Error GoTo ErrorHandler
‘ 現在のデータベース参照を取得(パフォーマンスとメモリの最適化)
Set db = CurrentDb
‘ トランザクションまたは一括処理の開始前にシステム更新フラグを意識
‘ 今回は単一テーブルの更新例として「T_SalesHeader」をターゲットにする
Dim targetTableName As String
targetTableName = “T_SalesHeader”
‘ テーブルの存在確認を行いつつTableDefを取得
Set tdf = db.TableDefs(targetTableName)
‘ 1. テーブル自体の「説明」を設定
Call SetPropertySafe(tdf, “Description”, dbText, “【基幹連携】売上ヘッダー情報を格納するマスタテーブル。夜間バッチで更新。”)
‘ 2. フィールドごとの「説明」を設定
For Each fld in tdf.Fields
Select Case fld.Name
Case “SalesID”
Call SetPropertySafe(fld, “Description”, dbText, “主キー: 売上を一意に識別するUUID文字列”)
Case “CustomerID”
Call SetPropertySafe(fld, “Description”, dbText, “外部キー: T_Customer.CustomerID とリレーション”)
Case “TotalAmount”
Call SetPropertySafe(fld, “Description”, dbText, “総金額(税込み)。計算フィールドではなくVBAで挿入時計算。”)
Case Else
‘ その他のフィールドに対するデフォルトの説明やスキップ処理
End Select
Next fld
MsgBox “メタデータの埋め込みが正常に完了しました。”, vbInformation, “アーキテクチャ通知”
CleanUp:
‘ 【重要】COMオブジェクトの明示的解放(メモリリークの完全阻止)
‘ ガベージコレクションに依存せず、VBAではローカル変数の参照を確実に破棄する
Set fld = Nothing
Set tdf = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error Number: ” & Err.Number & vbCrLf & _
“Description : ” & Err.Description, vbCritical, “致命的エラー”
Resume CleanUp
End Sub
‘ =========================================================================
‘ 内部関数 : SetPropertySafe
‘ 概要 : DAOオブジェクトの動的プロパティを安全に設定・追加する
‘ =========================================================================
Private Sub SetPropertySafe(obj As Object, propName As String, propType As Integer, propValue As Variant)
Dim prp As DAO.Property
Dim isFound As Boolean
isFound = False
‘ プロパティコレクションを走査
On Error Resume Next
Set prp = obj.Properties(propName)
If Err.Number = 0 Then
isFound = True
End If
Err.Clear
On Error GoTo 0
If isFound Then
‘ 既存の場合は値を更新
obj.Properties(propName).Value = propValue
Else
‘ 存在しない場合は新規作成して追加(エラー3270対策の核心)
Set prp = obj.CreateProperty(propName, propType, propValue)
obj.Properties.Append prp
Set prp = Nothing
End If
End Sub
—
3. チーフアーキテクトが解説するコードの急所
上記のコードが、そこらの入門書にあるコードと決定的に違う理由を解説する。
1. 動的プロパティの「遅延バインディング的」アプローチ
DAOの`Properties`コレクションは、ADOのそれとは異なり、未定義のプロパティをいきなり `.Properties(“Description”) = “…”` のように代入すると実行時エラー3270を吐いて即死する。
`SetPropertySafe`関数内での `On Error Resume Next` を用いた存在チェックと、存在しない場合の `CreateProperty` & `Append` の組み合わせこそが、レガシーDAOを完全に手なずける唯一の作法である。
2. メモリ最適化とオブジェクトのライフサイクル管理
Access VBAの背後でうごめくCOMコンポーネント(DAO Engine)は、適切な参照解放を行わないと、アプリケーション終了後もメモリ空間に残骸を残すことがある。特にループ処理や大規模なバッチ処理の最中、`Set tdf = Nothing` や `Set db = Nothing` をケチることは、メモリリークを自ら引き起こしていると同義である。
プロシージャの出口(`CleanUp:` ラベル)を必ず用意し、逆順にオブジェクトを破棄する徹底ぶりがプロのコードだ。
—
4. 応用:DB内ドキュメントをMarkdownやHTMLとして自動き出しする
この仕組みの真価は、「書き込める」ことだけではない。「DB内からメタデータを一瞬で抽出し、生きたドキュメントとして再構築できる」点にある。
もしチームメンバーが最新の仕様を確認したいのであれば、以下の要領でテーブルとフィールドの説明をイミディエイトウインドウ、あるいはテキストファイル(Markdown形式)へ吐き出させるスクリプトを常備しておくと良い。
Public Sub ExportDatabaseSchemaAsMarkdown()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim mdContent As String
Set db = CurrentDb
mdContent = “# データベース自動生成仕様書” & vbCrLf & vbCrLf
For Each tdf In db.TableDefs
‘ システムテーブル(MSysで始まるもの)は除外する
If Left(tdf.Name, 4) <> “MSys” And Left(tdf.Name, 1) <> “~” Then
mdContent = mdContent & “
テーブル: ” & tdf.Name & vbCrLf
‘ テーブル説明の取得(安全関数を使用)
mdContent = mdContent & “> ” & GetPropertySafe(tdf, “Description”) & vbCrLf & vbCrLf
mdContent = mdContent & “| フィールド名 | データ型 | サイズ | 説明 |” & vbCrLf
mdContent = mdContent & “| :— | :— | :— | :— |” & vbCrLf
For Each fld In tdf.Fields
Dim fldDesc As String
fldDesc = GetPropertySafe(fld, “Description”)
mdContent = mdContent & “| ” & fld.Name & ” | ” & GetTypeName(fld.Type) & ” | ” & fld.Size & ” | ” & fldDesc & ” |” & vbCrLf
Next fld
mdContent = mdContent & vbCrLf
End If
Next tdf
‘ デバッグ出力(必要に応じてファイル出力へ変更可能)
Debug.Print mdContent
Set db = Nothing
MsgBox “スキーマのMarkdown化が完了しました。イミディエイトウインドウを確認してください。”, vbInformation
End Sub
‘ プロパティを安全に取得するヘルパー
Private Function GetPropertySafe(obj As Object, propName As String) As String
Dim val As String
On Error Resume Next
val = obj.Properties(propName).Value
If Err.Number <> 0 Then val = “(未定義)”
On Error GoTo 0
GetPropertySafe = val
End Function
‘ DAOのデータ型IDを文字列に変換するヘルパー
Private Function GetTypeName(dataType As Integer) As String
Select Case dataType
Case dbBoolean: GetTypeName = “Yes/No”
Case dbByte: GetTypeName = “バイト”
Case dbInteger: GetTypeName = “整数”
Case dbLong: GetTypeName = “長整数”
Case dbCurrency: GetTypeName = “通貨”
Case dbSingle: GetTypeName = “単精度”
Case dbDouble: GetTypeName = “倍精度”
Case dbDate: GetTypeName = “日付/時刻”
Case dbText: GetTypeName = “テキスト”
Case dbMemo: GetTypeName = “メモ”
Case dbLongBinary: GetTypeName = “OLEオブジェクト”
Case Else: GetTypeName = “その他(” & dataType & “)”
End Select
End Function
—
総括:データベースに「文脈」を宿せ
システムが陳腐化する最大の原因は、コードやテーブルそのものではなく、「なぜその設計にしたのか」という文脈(コンテキスト)が失われることにある。
AccessのTableDefにおける「説明」プロパティをVBAで完全に掌握し、ビルドプロセスやイニシャライズルーチンの一部として組み込むこと。それは、単なるお片付けではない。レガシーシステムの寿命を延命させ、後続の開発者への最大のギフトとなる、極めて高度なエンジニアリングである。
コードを書きなぐり、動くだけのシステムを作る時代は終わった。
データベース自身に語らせよ。それが、真のプロフェッショナルの仕事である。
