スケジュール操作の「ブラックボックス化」を許すな:大規模プロジェクトにおける制約条件の変更履歴ログ設計
大規模なプロジェクト管理において、MS Projectのスケジュールは「聖域」であるべきだ。しかし、現実にはどうだろうか。
現場のPMやプランナーが、締め切りを守るために、あるいは進捗を良く見せるために、タスクの制約条件(Constraint Type)を「ASAP(可能な限り早く)」から「Must Finish On(○月○日終了必須)」へ、あるいはその逆に、こっそりと書き換える。
この「制約条件の操作」は、クリティカルパスを意図的にずらし、プロジェクトの真のリスクを隠蔽する行為に直結する。MS Project標準の監査機能だけでは、誰が、いつ、どのタスクの制約を、どのような意図(値)で変更したのかを、ガバナンスの観点から追跡することは不可能に近い。
本記事では、単なる「VBAによるログ出力」の解説はしない。大規模プロジェクトの整合性を守るための、「スナップショット比較型・変更検知アーキテクチャ」の構築手法を伝授する。
1. なぜ、一般的な「イベント駆動」の設計では破綻するのか
多くの開発者が最初に陥る罠がある。「タスクが変更された瞬間にログを取る」という発想だ。
しかし、MS ProjectのVBAオブジェクトモデルには、Excelの`Worksheet_Change`のような、タスクのプロパティ変更をリアルタイムに捕捉するグローバルなイベントハンドラが存在しない。
「じゃあ、定期的にループを回してチェックすればいいじゃないか」と考える。
それは、エンジニアとして三流の思考だ。
数千、数万タスクを抱える大規模プロジェクトにおいて、全タスクのプロパティを全件走査し、外部DBへ問い合わせる処理を頻繁に走らせれば、MS Projectの計算エンジン(Scheduling Engine)と競合し、アプリケーションのフリーズや、最悪の場合、データの破損を招く。
プロが採用すべき戦略:Snapshot & Diff(差分抽出)
我々が取るべき設計は、「状態の比較」である。
1. 前回実行時の状態(Snapshot)をDB、あるいはローカルのキャッシュに保持しておく。
2. 現在の状態(Current State)を読み出す。
3. 両者の差分(Diff)を計算し、「変化があったものだけ」をトランザクションとしてSQL Serverへ書き込む。
この設計こそが、パフォーマンスと整合性を両立させる唯一の道だ。
2. アーキテクチャ設計:疎結合な3レイヤー構造
堅牢なツールを作るには、ロジックを分離せよ。MS Projectのオブジェクトモデルに直接SQL文を書き込むようなコードは、保守性の観点から即座にゴミ箱へ捨てていい。
1. Data Access Layer (DAL): SQL Serverとの通信、Connectionの管理、Parameterized Query(パラメータ化クエリ)の実行を担当。
2. Business Logic Layer (BLL): MS ProjectのTaskオブジェクトから必要な情報を抽出し、前回状態との比較ロジックを実行。
3. Presentation/Trigger Layer: リボンボタンやアドインの実行トリガー。
3. 実装:プロダクション・クオリティのコード例
以下に、SQL Serverへ変更履歴を安全かつ高速に書き込むためのコア・ロジックを示す。ここでは、ADO(ActiveX Data Objects)を使用する。
SQL Server 側のテーブル定義例
まず、受け皿となるテーブルが必要だ。インデックスは、後日の監査(Audit)を高速化するために、`TaskID` と `ChangeDate` に適切に張っておくこと。
CREATE TABLE ProjectConstraintLogs (
LogID INT IDENTITY(1,1) PRIMARY KEY,
ProjectName NVARCHAR(255),
TaskID INT,
TaskName NVARCHAR(500),
OldConstraintType INT,
NewConstraintType INT,
OldConstraintDate DATETIME,
NewConstraintDate DATETIME,
ChangedBy NVARCHAR(100),
ChangedAt DATETIME DEFAULT GETDATE()
);
VBA 実装:Difference Engine & DB Logger
このコードは、単なるコピペ用ではない。設計思想を理解して使え。
Option Explicit
' ==============================================================================
' Module: modGovernanceEngine
' Description: スケジュール制約条件の変更を検知し、SQL Serverへログ出力する
' ==============================================================================
' 参照設定: Microsoft ActiveX Data Objects 6.1 Library
' 参照設定: Microsoft Project 16.0 Object Library
Private Const DB_CONN_STR As String = "Provider=SQLNCLI11;Server=YOUR_SERVER;Database=YOUR_DB;Trusted_Connection=yes;"
''' <summary>
''' メインエントリーポイント:制約条件の差分をチェックし、ログを記録する
''' </summary>
Public Sub SyncConstraintChanges()
Dim proj As MSProject.Project
Set proj = MSProject.ActiveProject
Dim task As MSProject.Task
Dim dbHandler As New clsDBHandler
Dim diffEngine As New clsDiffEngine
On Error GoTo ErrorHandler
Debug.Print "--- Audit Start: " & Now & " ---"
' 1. DB接続の確立(シングルトン的な管理を推奨)
If Not dbHandler.Connect(DB_CONN_STR) Then
MsgBox "Database connection failed.", vbCritical
Exit Sub
End If
' 2. 差分抽出ロジックの実行
' 注意:大規模プロジェクトでは、全タスクを回すのではなく、
' 「最終更新日」以降のタスクに絞り込むなどの最適化を検討せよ。
For Each task In proj.Tasks
If Not task Is Nothing Then
' 制約条件に変更があるか判定
If diffEngine.HasConstraintChanged(task, dbHandler) Then
' 変更があればログを記録
dbHandler.LogConstraintChange task, proj.Name
Debug.Print "Logged Change: " & task.Name
End If
End If
Next task
' 3. 後処理
dbHandler.Disconnect
MsgBox "Audit completed successfully.", vbInformation
CleanExit:
Exit Sub
ErrorHandler:
MsgBox "Critical Error: " & Err.Description, vbCritical
Resume CleanExit
End Sub
' ==============================================================================
' Class: clsDBHandler (Data Access Layer)
' ==============================================================================
' 役割: DB接続と、安全なパラメータ化クエリによるログ書き込み
' ==============================================================================
' (Class Moduleとして作成)
' [内部プロパティ・変数]
' Private conn As ADODB.Connection
Public Function Connect(ByVal connStr As String) As Boolean
' 接続ロジック(エラーハンドリング含む)
End Function
Public Sub LogConstraintChange(ByVal t As MSProject.Task, ByVal projName As String)
' ここで Parameterized Query を使用して SQL Injection を防ぐ
' 実行するSQLイメージ:
' INSERT INTO ProjectConstraintLogs (ProjectName, TaskID, TaskName, OldConstraintType, NewConstraintType, ...)
' VALUES (?, ?, ?, ?, ?, ...)
End Sub
' ==============================================================================
' Class: clsDiffEngine (Business Logic Layer)
' ==============================================================================
' 役割: 現在のTaskオブジェクトの状態と、DB上の最新状態を比較する
' ==============================================================================
Public Function HasConstraintChanged(ByVal t As MSProject.Task, ByRef db As clsDBHandler) As Boolean
' 1. DBから当該Taskの「最新の制約条件」を取得
' 2. t.ConstraintType および t.ConstraintDate と比較
' 3. 変化があれば True を返す
'
' 注意:ここでSelect Caseを多用するのではなく、
' 比較ロジックを極限まで軽量化することがパフォーマンスの鍵だ。
End Function
4. プロフェッショナルが守るべき「鉄則」
この仕組みを運用するにあたり、以下の3点を絶対に忘れるな。
① パラメータ化クエリ(Parameterized Query)の徹底
VBAでSQLを書く際、文字列連結(`”WHERE ID = ” & task.ID`)でクエリを組み立てる者は、セキュリティ意識が欠如していると見なされる。SQL Server側での型変換コストを下げ、SQLインジェクションを完全に防ぐため、必ず `ADODB.Command` オブジェクトを使用し、パラメータをバインドせよ。
② トランザクションの粒度
ログの書き込みは、タスクごとに `Commit` するのではなく、一連のチェックが終わった後にまとめて、あるいは一定件数ごとにバッチ処理として行うべきだ。I/O回数を減らすことが、大規模プロジェクトにおける「重いツール」を回避する唯一の手段である。
③ ユーザーへの「心理的抑止力」
このツールは、単なる記録用ではない。
「制約条件の変更はすべてDBに記録され、監査対象となる」という事実をユーザーに周知させること。これが、最も強力なガバナンス(統制)として機能する。
結びに代えて
業務自動化エンジニアの仕事は、単に「作業を楽にする」ことではない。
「システムの信頼性を担保し、ビジネスの意思決定に必要なデータの純度を守る」ことにある。
今回紹介したスナップショット比較型のアプローチは、実装の難易度は高い。しかし、一度構築してしまえば、それは組織にとっての強力な資産となる。妥協せず、堅牢な設計を貫け。
