【実務・中級編】【上級者向け】SQL Serverのトランザクションログと連携したプロジェクト保存のロールバック処理 – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:SQL Serverトランザクションログと同期する堅牢なプロジェクト保存・ロールバック設計

開発現場において、Microsoft Projectのデータ管理ほど頭を悩ませるものはない。
「ローカルの`.mpp`ファイルには保存されたが、進捗管理用のSQL Server側への書き込みでコケた」
「逆にDB側だけ更新され、肝心のファイル保存がディスク容量不足で失敗した」

この「ファイルとRDBの二重管理」が生む不整合は、業務システムの信頼性を根底から破壊する。片方だけが成功した状態(ダーティデータ)を放置すれば、翌日にはプロジェクトマネージャーからの冷徹な追及が待っているだろう。

今回は、Project VBAとSQL Serverのトランザクションログを高度に連携させ、「全か無か(All or Nothing)」を完全に担保する堅牢なロールバック機構の設計思想と実装を伝授する。

1. なぜ「普通のVBAコード」では破綻するのか?

多くの初級・中級プログラマが書くコードはこうだ。

‘ 【アンチパターン】絶対に真似してはいけない実装
Sub NaiveSaveProcess()
‘ 1. Projectファイルの保存
ActiveProject.SaveAs “C:\Projects\Target.mpp”

‘ 2. SQL Serverへの書き込み
Dim cn As Object
Set cn = CreateObject(“ADODB.Connection”)
cn.Open “Provider=SQLOLEDB;…”
cn.Execute “UPDATE ProjectStatus SET Progress = 100 WHERE…”

‘ 破綻ポイント:
‘ もし2番目のSQL実行時エラーで落ちたら?
‘ -> ファイルは上書き保存されたのに、DBは古いまま取り残される。
End Sub

このアプローチが非効率かつ危険な理由は明白である。
1. トランザクションの分離: ファイルシステム(OS)とRDB(SQL Server)という、全く異なるコンテキストの書き込みを直列に並べているだけで、不可分(原子性)の保証がない。
2. ロールバックの欠如: 途中で例外が発生した際、既に変更されたファイルを元の状態に戻す(あるいはDB側を差し戻す)ための補償トランザクションが考慮されていない。

真にプロフェッショナルな設計とは、「SQL Server側のトランザクションログ(Transaction Log)を主導権(マスタ)とし、VBA側でファイル操作の成否を監視して全体をロールバック・コミットする仕組み」である。

2. 堅牢なロールバック設計のアーキテクチャ

今回構築するシステムでは、以下のライフサイクルでデータの整合性を守る。

1. 事前バックアップ: 操作対象の`.mpp`ファイルを一時領域へ安全に退避(VBA側)。
2. DBトランザクション開始: `BEGIN TRANSACTION`により、SQL Server側で変更の隔離領域を確保。
3. DB更新処理: 進捗やメタデータをSQL Serverへ書き込み(ログに記録)。
4. Projectファイル保存: `ActiveProject.Save`を実行。
5. 判定とコミット/ロールバック:

  • 成功時: `COMMIT TRANSACTION`を発行し、一時ファイルを破棄。
  • 失敗時(トラップ発動): `ROLLBACK TRANSACTION`でDBを復元し、さらに退避させておいた一時ファイルで元の`.mpp`を上書き復元する。

この「二重の安全弁」があって初めて、エンタープライズ環境に耐えうるツールと言える。

3. 【プロダクションコード】完全同期・ロールバック実装

以下のコードは、エラーハンドリングとADOトランザクション、そしてファイルシステムの物理復元を組み合わせた実用コードである。標準モジュールに貼り付けて利用してほしい。

Option Explicit

‘==============================================================================
‘ プロジェクトファイルとSQL Serverトランザクションの同期保存・ロールバック処理
‘==============================================================================
Public Sub SaveProjectWithSQLTransaction()
Dim cn As Object
Dim targetFilePath As String
Dim backupFilePath As String
Dim fso As Object
Dim isTransStarted As Boolean

‘ ターゲットファイルパスの取得
If ActiveProject.Path = “” Then
MsgBox “プロジェクトファイルが一度も保存されていません。先に手動で保存してください。”, vbCritical, “致命的エラー”
Exit Sub
End If

targetFilePath = ActiveProject.FullName
backupFilePath = Environ(“TEMP”) & “\” & ActiveProject.Name & “.bak”

Set fso = CreateObject(“Scripting.FileSystemObject”)
Set cn = CreateObject(“ADODB.Connection”)

On Error GoTo ErrorHandler

‘ —————————————————-
‘ Step 1: 物理ファイルの事前バックアップ(ロールバック用)
‘ —————————————————-
If fso.FileExists(targetFilePath) Then
fso.CopyFile targetFilePath, backupFilePath, True
End If

‘ —————————————————-
‘ Step 2: SQL Server接続とトランザクション開始
‘ —————————————————-
‘ ※実際の環境に合わせて接続文字列を変更してください
cn.Open “Provider=MSOLEDBSQL;Server=myServerAddress;Database=myDatabase;Trusted_Connection=yes;”

cn.BeginTrans
isTransStarted = True

‘ —————————————————-
‘ Step 3: DB側の更新処理(例:プロジェクトメトリクスの記録)
‘ —————————————————-
Dim sql As String
sql = “UPDATE dbo.ProjectMaster SET LastSaved = GETDATE(), Status = ‘Locked’ WHERE ProjectCode = ‘” & ActiveProject.Code & “‘;”
cn.Execute sql

‘ —————————————————-
‘ Step 4: Projectファイルの保存実行
‘ —————————————————-
ActiveProject.Save

‘ —————————————————-
‘ Step 5: コミット(全処理成功)
‘ —————————————————-
cn.CommitTrans
isTransStarted = False

‘ バックアップ一時ファイルの削除
If fso.FileExists(backupFilePath) Then fso.DeleteFile backupFilePath, True

MsgBox “プロジェクトの保存とデータベースの同期が正常に完了しました。”, vbInformation, “成功”
GoTo CleanUp

ErrorHandler:
‘ —————————————————-
‘ 例外発生時のロールバック処理
‘ —————————————————-
Dim errDesc As String
errDesc = Err.Description

‘ DBトランザクションのロールバック
If isTransStarted Then
On Error Resume Next
cn.RollbackTrans
On Error GoTo 0
End If

‘ ファイルのロールバック(バックアップから復元)
If fso.FileExists(backupFilePath) Then
On Error Resume Next
fso.CopyFile backupFilePath, targetFilePath, True
‘ バックアップ削除
fso.DeleteFile backupFilePath, True
On Error GoTo 0
End If

MsgBox “エラーが発生したため、すべての変更をロールバックしました。” & vbCrLf & _
“詳細: ” & errDesc, vbCritical, “トランザクション中断”

CleanUp:
‘ 接続の解放
If Not cn Is Nothing Then
If cn.State = 1 Then cn.Close
Set cn = Nothing
End If
Set fso = Nothing
End Sub

4. チーフアーキテクトが教える実装上の極意・注意点

このコードを現場に導入する際、以下のポイントを怠ると予期せぬトラブルシューティングに追われることになる。

① 接続プロバイダの選定

コード内では `MSOLEDBSQL` (Microsoft OLE DB Driver for SQL Server)を指定している。古い `SQLOLEDB` は既に非推奨であり、SQL Serverのモダンなセキュリティ機能(TLS 1.2/1.3等)に対応していないため、必ず最新のプロバイダを使用すること。

② ロック競合の考慮(ISOLATION LEVEL)

SQL Server側で複数ユーザーが同時にプロジェクトデータを参照・更新している場合、トランザクションの競合(デッドロック)が発生する可能性がある。必要に応じて、ADO接続直後に以下のようなクエリを発行し、トランザクション分離レベルを調整する設計も視野に入れること。

cn.Execute “SET TRANSACTION ISOLATION LEVEL READ COMMITTED;”

③ ファイルシステム操作の遅延(I/Oレイテンシ)

ネットワークドライブ上に`.mpp`が存在する場合、`ActiveProject.Save` や `FSO.CopyFile` の完了をOSが検知する前に次の処理へ進み、ファイルハンドルが解放しきれずにエラーになるケースがある。大規模なプロジェクトファイルを扱う場合は、エラーハンドラ内でのわずか数ミリ秒のウェイト(`DoEvents`の活用など)が必要になることもある。

総括

VBAは「おもちゃのマクロ言語」ではない。適切なアーキテクチャとRDBのトランザクション理論を融合させれば、基幹システムに匹敵する堅牢なデータ整合性を担保できる。

「ファイルが壊れた」「DBとズレた」という現場の悲鳴を根絶するためにも、単なる`.Save`の呼び出しから脱却し、「トランザクションログ駆動型のセキュアな保存ルーチン」をあなたのプロジェクトに標準装備してほしい。

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