【Access VBA極限解説】マルチユーザー地獄からの脱出:DAO「dbConsistent」と「dbInconsistent」が握る競合制御の全貌
開発現場でよくある悪夢を思い出してほしい。
「ローカルでテストした時は完璧だったのに、本番環境の複数人で同時運用し始めた途端、データが上書きされて消えた」「わけのわからない実行時エラーで業務が止まった」——。
Accessを単なる「お蔵入りの個人用ツール」から、複数人が同時稼働する「実用的な業務システム」へと昇華させる時、避けて通れないのがマルチユーザー環境における更新競合(コンフリクト)の制御だ。
今回は、DAO(Data Access Objects)のレコードセット更新において、あまり語られることのない奥義――`dbConsistent`(一貫性)と`dbInconsistent`(非一貫性)の挙動の違いと、現場を破滅から救う堅牢なトランザクション設計について、チーフアーキテクトの私から直々に伝授しよう。
—
1. なぜ「なんとなくDAO」では現場で爆死するのか?
多くの初学者や、古いサンプルコードをコピペして満足しているプログラマーは、レコードセットを開く際に更新オプションを省略しがちだ。
‘ ありがちなアンチパターン
Set rs = db.OpenRecordset(“SELECT FROM T_受注 INNER JOIN T_顧客 ON …”, dbOpenDynaset)
このコード、何が問題か分かるか?
結合クエリ(JOIN)を含むレコードセットに対して更新を行う場合、Access(Jet/ACEエンジン)は「どのテーブルのどのカラムまで更新を許可するか」の制御を迫られる。ここでオプションを正しく指定していないと、予期せぬデータ不整合を引き起こすか、あるいは「他のユーザーが変更したデータ知らん顔して上書きする(いわゆるLast-Write-Winsの悲劇)」を引き起こすことになる。
複数テーブルをまたぐ更新において、データの「一貫性(Integrity)」をどこまで担保するかを明示的に指定するのが、`dbConsistent` と `dbInconsistent` なのだ。
—
2. アーキテクチャの核心:`dbConsistent` vs `dbInconsistent`
まずは、この2つのオプションの本質的な違いをロジカルに整理する。
| 比較項目 | `dbConsistent` (デフォルト) | `dbInconsistent` |
| :— | :— | :— |
| 対象データ | 「一貫性」が保たれる範囲のみ更新可能 | 結合元のすべてのテーブルを強制的に更新可能 |
| 多重JOIN時 | メイン(「一」側)テーブルのデータ変更に制限がかかる場合がある | 「多」側だけでなく「一」側のデータも単一の操作で書き換え可能になる |
| 安全性 | 高(リレーショナルな整合性をエンジンが守る) | 中〜低(開発者の意図しない副作用を生みやすい) |
| 現場の推奨度 | 基本はこれ一択 | 特殊なバッチ処理や特権的な一括更新のみ限定使用 |
なぜ `dbConsistent` を選ぶべきなのか?
リレーショナルデータベースの本質は「整合性」だ。`dbConsistent` を指定した場合、結合クエリの「一対多」の関係において、親テーブル(一側)のデータ構造上の整合性が崩れるような更新をエンジンがブロックしてくれる。
一方、`dbInconsistent` は強力だが危険だ。これを安易に使うと、子テーブルを更新するつもりが、意図せず親テーブルのマスターデータまで歪めてしまうリスクがある。「動けばいい」という甘い設計が、後日データ破損という致命傷になって返ってくるのはこのためだ。
—
3. 実践:競合制御を組み込んだ堅牢なプロダクションコード
百聞は一見にしかず。実務でそのまま使える、堅牢なエラーハンドリングと `dbConsistent` を明示した更新処理のコードテンプレートを提示する。
このコードは、楽観的排他制御の思想を取り入れつつ、DAOのトランザクションと競合エラー(Error 3197など)を確実に捕捉・制御するプロフェッショナル仕様だ。
‘ ==============================================================================
‘ プロシージャ名 : UpdateOrderStatusWithConsistent
‘ 概要 : DAOとdbConsistentを用いた堅牢なマルチユーザー対応更新処理
‘ 備考 : 競合発生時はユーザーに再確認を促すか、安全にロールバックする
‘ ==============================================================================
Public Sub UpdateOrderStatusWithConsistent(ByVal targetOrderID As Long, ByVal newStatus As String)
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String
On Error GoTo ErrorHandler
‘ データベースオブジェクトの取得(カレントDB)
Set db = CurrentDb
‘ 結合クエリを dbOpenDynaset かつ dbConsistent でオープン
‘ (受注テーブルと顧客テーブルを結合したビューを想定)
strSQL = “SELECT O.OrderID, O.OrderStatus, C.ClientName, O.UpdateTime ” & _
“FROM T_受注 AS O INNER JOIN T_顧客 AS C ON O.ClientID = C.ClientID ” & _
“WHERE O.OrderID = ” & targetOrderID
‘ 【重要】dbConsistentを指定し、リレーショナル整合性を守る
Set rs = db.OpenRecordset(strSQL, dbOpenDynaset, dbConsistent)
If rs.EOF And rs.BOF Then
MsgBox “対象のデータが見つかりませんでした。すでに削除されている可能性があります。”, vbExclamation, “排他制御”
GoTo Cleanup
End If
‘ トランザクションの開始(複数テーブル・行の整合性を担保)
db.BeginTrans
‘ 編集モードへ移行
rs.Edit
‘ データの書き換え
rs!OrderStatus = newStatus
rs!UpdateTime = Now() ‘ 更新日時の打刻
‘ 更新のコミット実行
rs.Update
‘ トランザクションの確定
db.CommitTrans
MsgBox “データの更新が正常に完了しました。”, vbInformation, “成功”
Cleanup:
‘ リソースの確実な解放(メモリリークの根絶)
On Error Resume Next
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
‘ トランザクション中のエラーであればロールバック
db.RollbackTrans
‘ DAO特有の競合エラー(3197: 他のユーザーがデータを変更しました)のハンドリング
If Err.Number = 3197 Then
MsgBox “他のユーザーが同時刻にこのデータを更新しました。” & vbCrLf & _
“最新のデータを取得し直してやり直してください。”, vbCritical, “更新競合エラー”
Else
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“エラー内容: ” & Err.Description, vbCritical, “システムエラー”
End If
Resume Cleanup
End Sub
—
4. チーフアーキテクトからの現場への提言
プログラミングにおいて「動くコードを書くこと」はスタートラインに過ぎない。真に価値があるのは、「環境が荒れても、同時アクセスが集中しても壊れないコード」だ。
今回のポイントを最後に総括する。
1. 結合クエリの更新には必ずオプションを意識せよ
デフォルト任せにするのではなく、業務要件に合わせて `dbConsistent` を明示し、データ構造の整合性をデータベースエンジンに守らせろ。
2. トランザクションとエラーハンドリングはセットで書け
「エラーが出たらロールバック、成功したらコミット」。この基本原則を怠ったシステムは、いつか必ずデータベースをゴミの山に変える。
3. エラー 3197 を恐れるな、むしろ歓迎しろ
マルチユーザー環境で競合が起きるのは健全な証拠だ。それを黙殺して上書きするような実装(`dbInconsistent` の安易な乱用など)は、エンジニアの怠慢でしかない。競合を検知し、ユーザーに優しくハンドリングする設計こそが、プロの仕事である。
Accessというコンパクトなプラットフォームであっても、設計思想はエンタープライズシステムと何ら変わらない。この知見をあなたの武器とし、現場の信頼を勝ち取る強固なシステムを構築してほしい。
