プロジェクトの神に捧ぐ:VBA×SQL Server「双方向同期エンジン」の極意
多くのエンジニアが、MS Projectの管理画面とSQL Serverのテーブルを「手動」で統合しようとして泥沼にハマる。エクスポートしてインポートして、IDの不整合に頭を抱え、結局は「最新版がどれか分からない」という悲劇。
本稿では、Project VBAのライフサイクルをハックし、「保存トリガーによる双方向同期エンジン」を実装するアーキテクチャを提示する。単なるコードの貼り付けではない。堅牢なシステムを構築するための「設計思想」を刻み込んでほしい。
—
1. なぜ「力技」が失敗するのか:同期の鉄則
双方向同期で最も避けるべきは、無限ループとデータ競合だ。「Projectで保存→DBへ書き込み→DBのタイムスタンプ更新→Projectで読み込み→保存…」というループに陥れば、システムは即死する。
- 鉄則1:一意なキー管理 (UIDの神聖化)
Projectの`Task.UniqueID`をそのままDBの主キーにせよ。これを変更する権限は、VBA以外の何者にも与えてはならない。
- 鉄則2:変更検知のメタデータ
DBテーブルには必ず `LastModifiedDate` と `SyncVersion` を保持させる。Projectの`Task.Modified`プロパティと照合し、差分のみを転送するデルタ更新こそが、パフォーマンスを担保する唯一の道だ。
- 鉄則3:アトミックなトランザクション
VBA側で `Application.ActiveProject.ProjectBeforeSave` イベントをフックし、一連の同期プロセスをトランザクション制御下で完結させる。
—
2. 【核心】同期エンジンの実装コード
以下のコードは、Projectの標準モジュール `ThisProject` に記述する。データベース操作には `ADODB` を使用するが、接続文字列は適宜環境に合わせて変更せよ。
‘ — ThisProject モジュール —
‘ プロジェクト保存直前に同期を走らせる
Private Sub Project_BeforeSave(ByVal pj As Project, ByVal SaveAsUi As Boolean, Cancel As Boolean)
On Error GoTo ErrorHandler
‘ 同期エンジンを起動
Call SyncEngine.PushProjectToSQL(pj)
Exit Sub
ErrorHandler:
MsgBox “同期中に致命的なエラーが発生しました: ” & Err.Description, vbCritical
Cancel = True ‘ 同期失敗時は保存をキャンセルさせる(データ不整合を防ぐため)
End Sub
‘ — SyncEngine (標準モジュール) —
Public Sub PushProjectToSQL(pj As Project)
Dim conn As Object
Dim tsk As Task
Dim strSQL As String
Set conn = CreateObject(“ADODB.Connection”)
conn.Open “Provider=SQLOLEDB;Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;Integrated Security=SSPI;”
For Each tsk In pj.Tasks
If Not tsk Is Nothing Then
‘ デルタ更新:UIDをキーにUPSERTを実行
strSQL = “MERGE INTO ProjectTasks AS Target ” & _
“USING (SELECT ” & tsk.UniqueID & ” AS UID) AS Source ” & _
“ON Target.UID = Source.UID ” & _
“WHEN MATCHED THEN UPDATE SET TaskName = ‘” & Replace(tsk.Name, “‘”, “””) & “‘, Finish = ‘” & tsk.Finish & “‘ ” & _
“WHEN NOT MATCHED THEN INSERT (UID, TaskName, Finish) VALUES (” & tsk.UniqueID & “, ‘” & Replace(tsk.Name, “‘”, “””) & “‘, ‘” & tsk.Finish & “‘);”
conn.Execute strSQL
End If
Next tsk
conn.Close
End Sub
—
3. プロダクション環境で生き残るための注意点
このコードを動かすだけなら初級者でもできる。しかし、プロは以下のポイントを必ず設計に組み込む。
A. 非同期実行の検討
タスク数が数千件を超えると、保存時のVBA実行はGUIをフリーズさせる。大規模環境では、CSV経由でのバルクインサート(`bcp`ユーティリティ)を呼び出す設計に切り替えるべきだ。VBAは「トリガー」に徹し、重い処理は外部プロセスに委ねる。
B. 競合解決戦略(Conflict Resolution)
「DB側で修正し、Project側でも修正した」場合はどうするか?
- タイムスタンプ優先主義: 常に最新の更新時刻を持つ方を正とする。
- ログ出力: 衝突したタスクIDを別テーブル(`SyncConflictLog`)に書き出し、ユーザーにUI上で選択させる。これが「堅牢」なシステムの定義だ。
C. セキュリティとパフォーマンス
SQLインジェクション対策を怠るな。上記のコード例は概念実証用(PoC)である。実務では必ず `ADODB.Command` オブジェクトと `Parameters` コレクションを使用してSQLをプリコンパイルし、クエリプランのキャッシュ効率を最大化せよ。
—
最後に:エンジニアとしての矜持
VBAは、MS Projectという巨大なブラックボックスを制御するための極めて強力なインターフェースだ。しかし、このインターフェースを使う者は、同時に「データの整合性」という重い責任を背負う。
今回提示した「保存トリガー」による同期は、システムの一部に過ぎない。君たちが目指すべきは、ツールを作ることではなく、「プロジェクト管理という非定型業務に、SQLの厳密な論理をインストールすること」だ。
コードを書き換える前に、まずビジネスプロセスのフローを紙に書き出し、どこでデータが汚れるかを定義せよ。その設計図さえあれば、VBAは必ず君の最強の武器となるはずだ。
