【実務・中級編】【中級】テーブル定義変更時の「トランザクション制御」:失敗時にロールバックする仕組み – Access VBA解析バイブル

スポンサーリンク

Access VBAで「テーブル定義変更」を破壊させない:トランザクション設計の極意

Accessでシステムを構築していると、避けて通れないのが「テーブル定義の動的な変更」だ。運用開始後に「カラムを追加したい」「インデックスを張り直したい」といった要件は必ず発生する。

しかし、多くの開発者はここで大きな過ちを犯す。DDL(Data Definition Language)操作を何の保護もなしに実行するのだ。

もし、テーブルの構造変更中にネットワークが瞬断したり、Accessがハングアップしたらどうなるか? データベースは「中途半端な状態(不整合)」で放置され、最悪の場合、ファイル全体が破損する。

本稿では、プロフェッショナルとして守るべき「DDL操作におけるトランザクション制御」の真髄を伝授する。

—

なぜ「DAOのトランザクション」だけでは不十分なのか

まず理解してほしいのは、Access(Jet/ACEエンジン)の`BeginTrans` / `CommitTrans`は、主にDML(データ操作:Insert, Update, Delete)を保護するためのものだ。

残念ながら、`TableDefs`オブジェクトを直接操作するDDL処理の多くは、`CommitTrans`で完全に保護しきれない挙動を示すことがある。特に「テーブルそのものの削除・再作成」や「複雑なリレーションシップの再構築」を行う際、中途半端に構造が変わった状態でロックがかかると、復旧不能なダメージを負う。

極限の設計指針:
1. 「作業用テンポラリテーブル」を経由する(既存テーブルを直接弄らない)
2. エラー発生時は、変更を破棄して「元の状態」を強制復元する
3. DAO.Databaseオブジェクトを適切に再利用し、メモリリークを防ぐ

—

実践:堅牢なDDLトランザクション・テンプレート

以下に、実務でそのまま使える「安全にテーブル構造を変更する」ためのパターンを示す。このコードの肝は、構造変更に失敗した際、確実に旧テーブルを保護する「バックアップ・ロールバック」の思想にある。

‘ —————————————————————–
‘ テーブル定義変更用:安全なトランザクション・テンプレート
‘ —————————————————————–
Public Sub SafeModifyTableStructure()
Dim db As DAO.Database
Dim td As DAO.TableDef
Dim isTransactionStarted As Boolean

Set db = CurrentDb

On Error GoTo ErrorHandler

‘ 1. トランザクション開始
DBEngine.BeginTrans
isTransactionStarted = True

‘ 2. バックアップの作成(失敗時の保険)
‘ 既存テーブルをリネームしてバックアップとするのが最も堅牢
DoCmd.CopyObject , “T_Target_Backup”, acTable, “T_Target”

‘ 3. DDL操作の実行
‘ 例:フィールド追加
db.Execute “ALTER TABLE T_Target ADD COLUMN NewField TEXT(50)”, dbFailOnError

‘ 4. コミット
DBEngine.CommitTrans
isTransactionStarted = False

‘ 成功したらバックアップを削除(不要であれば残しても良い)
DoCmd.DeleteObject acTable, “T_Target_Backup”

MsgBox “構造変更が完了しました。”, vbInformation
Exit Sub

ErrorHandler:
‘ 5. ロールバック処理
If isTransactionStarted Then
DBEngine.Rollback
End If

‘ バックアップから復旧を試みる
If ObjectExists(“T_Target_Backup”) Then
On Error Resume Next
DoCmd.DeleteObject acTable, “T_Target”
DoCmd.Rename “T_Target”, acTable, “T_Target_Backup”
End If

MsgBox “致命的なエラーが発生しました。変更はロールバックされました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“説明: ” & Err.Description, vbCritical
End Sub

‘ 補足:オブジェクト存在チェック関数
Private Function ObjectExists(objName As String) As Boolean
ObjectExists = (DCount(“”, “MSysObjects”, “Name='” & objName & “‘”) > 0)
End Function

—

現場で差がつく3つの視点

1. `dbFailOnError` の徹底

`db.Execute` を使う際、第二引数に `dbFailOnError` を指定していないコードは「論外」だ。これがないと、SQL実行時にエラーが発生してもVBA側で捕捉できず、コードが何事もなかったかのように次へ進んでしまう。業務自動化ツールにおいて、これは「サイレントバグ」の温床となる。

2. リレーションシップの扱い

DDLでテーブルを弄る際、リレーションシップが張られていると「テーブルの変更」そのものが拒絶される。事前に `db.Relations` を走査し、関連するリレーションを削除してから変更を行い、最後に再構築するロジックを組むべきだ。この「掃除」を自動化できるかどうかが、エンジニアの腕の見せ所である。

3. 排他制御を意識せよ

DDL操作はデータベース全体に対して強いロックをかける。共有環境(ネットワーク上の共有フォルダ)で実行する場合、他に開いているユーザーが一人でもいると失敗する。必ず `db.TableDefs.Refresh` を行い、エラーハンドラで「ユーザーが使用中」である旨を明示的に伝えるUIを設計すること。

結びに代えて

テーブル定義の変更は、データベースにとっての「手術」だ。安易に `ALTER TABLE` を乱発するのではなく、常に「失敗したらどう戻すか」という設計思想を持ってほしい。

真の業務自動化エンジニアとは、「コードが動くこと」を喜ぶのではなく、「コードが壊れたときに、いかにシステムを安全な状態へ誘導するか」を設計できる者を指す。このアーキテクチャを理解し、堅牢なシステムを構築してほしい。応援している。

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