【実務・中級編】【中級】DAO.Field.Attributesを解析し、自動採番(AutoNumber)フィールドをVBAで特定する汎用処理 – Access VBA解析バイブル

スポンサーリンク

【中級】DAO.Field.Attributesを徹底解剖:自動採番(AutoNumber)フィールドをVBAで完壁に特定する汎用設計

開発現場で汎用的なデータインポートツールや、動的なSQL生成エンジンを作ったことがある者なら一度は直面する壁がある。
それは、「どのフィールドが自動採番(AutoNumber)なのかを、どうやってプログラム側から確実に判定するか」という問題だ。

「テーブル定義なんて見れば一発じゃないか」と思ったそこのあなた。
では、ユーザーが勝手にフィールド名を変更したり、外部からリンクテーブル経由でデータを受け取ったりするマルチテナントな環境でも、その「目視の確信」は通用するだろうか?

今回は、Access VBAの真骨頂であるDAO(Data Access Objects)の深部、`Field.Attributes`プロパティを解析し、自動採番フィールドを寸分の狂いもなく動的に特定する「プロダクション品質」の汎用処理を伝授する。

なぜ「愚直なフィールド名推測」は破綻するのか

素人プログラマーや初学者がやりがちな最悪のアンチパターンがこれだ。

‘ 【絶対やってはいけない愚行】フィールド名で決め打ちする
If fld.Name = “ID” Or fld.Name = “Code” Or fld.Name = “採番” Then
‘ 自動採番とみなす処理…
End If

言語道断である。
現場のデータベース設計者は、主キーに「ID」と名付けるとは限らない。「T_ID」「No」「MemberCode」かもしれない。さらに言えば、自動採番フィールドの名前が「AutoNo」である保証などどこにもない。

また、インポート処理において、自動採番フィールドに対して明示的に値をINSERTしようとして「追記できません。自動採番フィールド ‘XXX’ には値を設定できません。」という、Accessデータベースエンジン(Jet/ACE)からの冷徹なエラーに阻まれた開発者は数知れない。

我々は、名前で推測するのではなく、データベースメタデータの本質(属性)をコードで直接読み取らなければならない。

核心:`DAO.Field.Attributes` とは何か

DAOの `Field` オブジェクトには、そのフィールドの性質をビット演算で保持する `Attributes` プロパティが存在する。

自動採番フィールドを判定するためには、この `Attributes` の中から特定のフラグ(定数)が立っているかをマスク処理(Bitwise AND演算)で検証する必要がある。

Access DAOにおける主な属性定数は以下の通りだ:

  • `dbAutoIncrField` (16): 自動増分(AutoNumber)フィールド
  • `dbUpdatableField` (32): 更新可能フィールド
  • `dbFixedField` (1): 固定長フィールド

我々が狙い撃つべきは、この中の `dbAutoIncrField` である。

実装:堅牢な自動採番特定モジュール

ここからが本題だ。コピペでそのまま業務システムに組み込める、極めて堅牢でパフォーマンスに配慮したファンクションを提示する。

無駄なエラーハンドリングを排除し、かつ想定外のオブジェクト破棄(メモリリーク)を防ぐため、DAOの参照は適切に解放する設計(クリーンアップの徹底)にしている。

Option Compare Database
Option Explicit

‘ =========================================================================
‘ 汎用モジュール: データベース構造解析
‘ 目的: 指定されたテーブル内の自動採番(AutoNumber)フィールド名を返す
‘ =========================================================================

Public Function GetAutoNumberFieldName(ByVal tableName As String) As String
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim resultName As String

resultName = “”

On Error GoTo ErrorHandler

‘ CurrentDbは呼び出しごとにインスタンスが生成されるため、
‘ 変数に保持してトランザクションやオブジェクトのライフサイクルを制御する
Set db = CurrentDb()

‘ テーブルが存在するか事前に確認(TableDefsコレクションの直接参照はエラーを生むため走査)
If Not TableExists(db, tableName) Then
Err.Raise 3265, “GetAutoNumberFieldName”, “指定されたテーブル ‘” & tableName & “‘ が存在しません。”
End If

Set tdf = db.TableDefs(tableName)

‘ 全フィールドを走査し、Attributesをビット演算で解析
For Each fld In tdf.Fields
‘ 【核心のロジック】
‘ Attributesと dbAutoIncrField のANDを取ることで、自動採番フラグの有無を判定
If (fld.Attributes And dbAutoIncrField) = dbAutoIncrField Then
resultName = fld.Name
Exit For ‘ 自動採番は通常1つのテーブルに1つなので見つかったら抜ける
End If
Next fld

CleanUp:
‘ オブジェクトの明示的な解放(Access VBAにおけるメモリ管理の鉄則)
Set fld = Nothing
Set tdf = Nothing
Set db = Nothing

GetAutoNumberFieldName = resultName
Exit Function

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “システムエラー”
Resume CleanUp
End Function

‘ ————————————————————————-
‘ ヘルパー関数: テーブル存在確認
‘ ————————————————————————-
Private Function TableExists(ByVal db As DAO.Database, ByVal tableName As String) As Boolean
Dim tdf As DAO.TableDef
Dim exists As Boolean

exists = False
For Each tdf In db.TableDefs
If tdf.Name = tableName Then
exists = True
Exit For
End If
Next tdf

TableExists = exists
End Function

この設計が「プロダクション品質」である理由

1. 名前依存からの完全な脱却
フィールド名が何であろうと、`dbAutoIncrField` フラグを直接監視しているため、スキーマ変更に対して圧倒的な耐性を持つ。
2. 安全なオブジェクトライフサイクル管理
VBAのガベージコレクションに頼らず、`Set xxx = Nothing` を徹底している。これを怠ると、Accessのコンパイルエラーやメモリリークを引き起こし、長期間稼働するシステムで致命的な「リソース不足」を誘発する。
3. `CurrentDb` の正しいハンドリング
`CurrentDb` は呼び出すたびに新しいDAO.Databaseオブジェクトをヒープに生成する。場当たり的にあちこちで `CurrentDb.TableDefs…` と書くのはパフォーマンス上の悪手である。一度変数に受けて使い回すのが、プロフェッショナルのコードだ。

実践:データインポートツールへの応用

この `GetAutoNumberFieldName` 関数を手に入れたことで、例えばExcelや外部CSVからデータをインポートするSQLを動的に構築する際、以下のようなスマートなアプローチが可能になる。

Public Sub ImportDataSafely(ByVal targetTable As String, ByVal importedData As DAO.Recordset)
Dim autoNumField As String

‘ 自動採番フィールドの特定
autoNumField = GetAutoNumberFieldName(targetTable)

If autoNumField <> “” Then
Debug.WriteLine “検出された自動採番フィールド: ” & autoNumField
‘ インポート処理のSQL構築時に、このフィールドをINSERT対象から除外するロジックを組む
Else
Debug.WriteLine “このテーブルに自動採番フィールドはありません。”
End If

‘ 以下、インポートの実処理へ…
End Sub

アーキテクトからの提言

Access VBAは「おもちゃの言語」と揶揄されることがある。しかしそれは、書く人間がオブジェクトモデルのライフサイクルや内部構造(DAOのプロパティ構造など)を理解せず、場当たり的なコードを量産しているからに他ならない。

データベースの仕様をコード側が「知る」のではなく、データベース自身に「語らせる」。
この設計思想をあなたのアプリケーションに組み込んだ瞬間から、保守に怯える日からは解放されるはずだ。プロとしての誇りを持ったコードを、次の現場でも実装してほしい。

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