こんにちは!Access VBAの現場へようこそ。
今回は、実務で避けて通れない、しかし一歩間違えるとシステム全体をクラッシュさせかねない「マルチユーザー環境でのテーブル定義変更と排他ロック管理」について、徹底的に解説していきます。
マクロの記録から脱却し、本格的な業務システムを作り始めると、必ずこんな壁にぶつかります。
> 「ローカルでは動くのに、共有サーバーに置いた途端『他のユーザーが使用中』エラーでテーブルが変更できない……」
安心してください。今回は、数々の修羅場を潜り抜けてきたプロのアーキテクトが、安全かつ確実にテーブル構造をアップデートする極意を伝授します。ここをクリアすれば、あなたのAccess VBAのスキルは間違いなく一段階上のステージに到達しますよ!
—
なぜマルチユーザー環境のテーブル変更は難しいのか?
まずは敵を知ることから始めましょう。
Access(特に.accdb)は、ファイル共有型のデータベースです。複数人が同時に同じファイルを開いているとき、誰かがテーブルのデザイン(列の追加や型変更など)を変更しようとすると、Accessはファイル全体、あるいは対象テーブルへの「排他ロック(Exclusive Lock)」を要求します。
もし、他のユーザーがフォームを開いていたり、レコードを参照・編集中だったりすると……?
容赦なく「実行時エラー:他のユーザーまたはプロセスが使用しています」が発生し、VBAコードは無残にクラッシュします。
これを力技で解決しようとしてはいけません。プロのエンジニアは、「接続ユーザーの存在を検知し、安全にロックを取得するか、あるいは優しく処理を中断する」というスマートな防衛策をコードに実装します。
—
プロが実践する!排他ロック制御の3ステップ
安全なテーブル定義変更を実現するためには、以下の3ステップを踏みます。
1. カレントデータベースの排他制御の仕組みを理解する
2. エラートラップ(Err.Number 3262 など)を活用したリトライ・離脱ロジックを作る
3. 安全に TableDef オブジェクトを操作する
言葉で説明するよりも、実際に現場で使える実用コードを見ていただきましょう。以下のコードは、マルチユーザー環境を考慮して「安全にフィールドを追加する」プロシージャです。
実用VBAコード:安全なフィールド追加サンプル
Public Sub SafeAddColumnToTable(ByVal tableName As String, ByVal fieldName As String, ByVal dataType As Integer)
‘ —————————————————————-
‘ 目的: マルチユーザー環境を考慮し、安全にテーブルへフィールドを追加する
‘ —————————————————————-
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim fld As DAO.Field
Dim retryCount As Integer
Const MAX_RETRY As Integer = 3 ‘ 最大リトライ回数
‘ エラーハンドラの設定(ここが命綱です)
On Error GoTo ErrorHandler
‘ カレントデータベースの参照を取得
Set db = CurrentDb()
‘ リトライループの開始(他のユーザーが一時的に占有している場合の対策)
retryCount = 0
TryLock:
‘ テーブル定義の取得(ここで排他ロック競合が発生しやすい)
Set tdf = db.TableDefs(tableName)
‘ すでにフィールドが存在するかチェック
If Not FieldExists(tdf, fieldName) Then
‘ フィールドの新規作成(例:テキスト型、サイズ50)
Set fld = tdf.CreateField(fieldName, dataType, 50)
‘ テーブル定義へフィールドを追加(ここで排他ロックが確定)
tdf.Fields.Append fld
tdf.Fields.Refresh
MsgBox “テーブル ‘” & tableName & “‘ にフィールド ‘” & fieldName & “‘ を追加しました。”, vbInformation, “成功”
Else
MsgBox “指定されたフィールドは既に存在します。”, vbExclamation, “通知”
End If
CleanUp:
‘ オブジェクトの解放(メモリリークとロック残留を防ぐプロの作法)
Set fld = Nothing
Set tdf = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
‘ エラー番号 3262: 他のユーザーがロックしています
‘ エラー番号 3211: テーブルをロックできません
If Err.Number = 3262 Or Err.Number = 3211 Then
retryCount = retryCount + 1
If retryCount <= MAX_RETRY Then
' 2秒待機してリトライ(ユーザーに優しく、システムを壊さない)
MsgBox "他のユーザーがテーブルを使用中のため、リトライします (" & retryCount & "/" & MAX_RETRY & ")", vbExclamation, "排他制御"
DoEvents
Application.Wait (Now + TimeValue("0:00:02"))
Resume TryLock
Else
MsgBox "他のユーザーがテーブルを使用しているため、処理を中断しました。時間を置いて再度実行してください。", vbCritical, "エラー"
Resume CleanUp
End If
Else
' その他の予期せぬエラー
MsgBox "予期せぬエラーが発生しました: " & Err.Description, vbCritical, "致命的エラー"
Resume CleanUp
End If
End Sub
' 補助関数:フィールドが存在するか判定
Private Function FieldExists(tdf As DAO.TableDef, fieldName As String) As Boolean
Dim f As DAO.Field
FieldExists = False
For Each f In tdf.Fields
If StrComp(f.Name, fieldName, vbTextCompare) = 0 Then
FieldExists = True
Exit For
End If
Next f
End Function
---
コードの深掘りポイント(エンジニアの知見)
上記のコードには、単なる「動くコード」を超えた、プロフェッショナルなこだわりが詰まっています。
1. `Application.Wait` と `DoEvents` によるマナーある待機
一瞬でエラーを返すのではなく、「少し待てば相手の処理が終わるかもしれない」というマルチユーザー環境特有のタイムラグを考慮しています。また、`DoEvents`を入れることで、待機中にAccessが「応答なし」になるのを防ぎます。
2. オブジェクトの徹底的な解放(`Set … = Nothing`)
Access VBAでは、DAOのオブジェクト変数をメモリに残したままにすると、暗黙的なロックやメモリリークの原因になります。処理の最後(`CleanUp`ラベル)で必ず変数を初期化する癖をつけましょう。
3. トランザクションとDDLの限界を知る
「じゃあトランザクション(`BeginTrans`)で囲めば安全なんじゃないの?」と思った方、素晴らしい着眼点です!
しかし、AccessのDAOにおけるテーブル定義の変更(TableDefsやFieldsの追加・削除)は、トランザクションの対象外であることが多いです。だからこそ、今回紹介したような「排他エラーをキャッチしてリトライする仕組み」が実務では必要不可欠になります。
—
さらに安全性を高めるためのアーキテクチャ設計
もしあなたが「これから本格的なシステムを構築する」という段階であれば、コードによる排他制御に頼るだけでなく、設計思想そのものを変えることを強くお勧めします。
- バックエンド(データ)とフロントエンド(UI/VBA)の完全分離
- ユーザー全員が同じAccessファイル(.accdb)を触るのではなく、テーブルだけの「バックエンドDB」をサーバーに置き、各クライアントは「フロントエンドDB」からリンクテーブル経由でアクセスする構成(いわゆる分割構成)にします。
- マスターデータの自動アップデート機構
- テーブル定義を頻繁に変える必要があるシステムでは、フロントエンド起動時にローカルのリンクテーブルやローカルキャッシュ構造を自動チェックし、必要に応じて裏側でサイレントアップデートする仕組みを構築するのがモダンなアプローチです。
—
まとめ
今回は、マルチユーザー環境におけるテーブル定義変更と排他ロック管理について解説しました。
- 複数人での共有環境では、テーブル変更時に必ず競合エラー(3262等)が起きることを前提にする。
- エラーハンドリングとリトライロジックを組み込み、システムとユーザーの双方に優しいコードを書く。
- 使い終わったDAOオブジェクトは必ず `Nothing` で解放する。
ここをクリアできれば、あなたの書くAccess VBAは、おもちゃのマクロから「堅牢な業務アプリケーション」へと見違えるほど進化します。
ぜひ、次の開発案件からこのコードと設計思想を取り入れてみてください。「おっ、分かってるね!」と周りから一目置かれるエンジニアになれますよ。それでは、次の現場でお会いしましょう!
