【入門編】【プロ向け】大規模プロジェクトにおける制約条件の変更履歴をSQL Serverへ保存する監査ログシステムの構築 – Project VBA解析バイブル

スポンサーリンク
スポンサーリンク

【プロ向け】大規模プロジェクトの命運を握る「変更履歴」の管理術:MS Project VBA × SQL Server 監査ログ構築ガイド

「マクロの記録」ボタンを押して、それっぽいコードが生成される。
そこから一歩踏み出し、「自分でロジックを組み立てる」フェーズへようこそ。

こんにちは。今日は、単なる自動化の枠を超えた、「エンタープライズ・レベルのシステム構築」という非常にエキサイティングなテーマについてお話ししましょう。

大規模なプロジェクト(数千タスク規模)を運用している現場では、一つの致命的な問いが常に突きつけられます。
「誰が、いつ、なぜ、このタスクの制約条件(Constraint)を変えたのか?」

スケジュール計算の根幹である「ASAP(なるべく早く)」や「Must Start On(この日に開始)」といった制約条件が、誰にも知られず書き換えられたとき、プロジェクト全体のクリティカルパスは崩壊し、納期遅延という名のパニックが引き起こされます。

今回は、MS Project VBAを使って、これらの変更を検知し、堅牢なSQL Serverへと記録する「監査ログシステム」の構築方法を、基礎から設計思想まで丁寧に解説していきます。

1. そもそも「制約条件(Constraints)」とは何か?

コードを書く前に、まずは私たちが制御しようとしている対象を正しく理解しましょう。

MS Projectにおける「制約条件」とは、スケジュール計算エンジンに対する「命令」です。

  • ASAP (As Soon As Possible): 「前工程が終わったらすぐ始めて!」という、最も自然な状態です。
  • ALAP (As Late As As Possible): 「できるだけ遅く始めて!」という、バッファを使い切る設定です。
  • Must Start On / Must Finish On: 「何があってもこの日にやって!」という、計算エンジンによる自動調整を拒否する強力な命令です。

これらは、タスクのプロパティ `ConstraintType` として管理されています。初心者のうちは「日付を直接書き換える」ことばかりに目が行きがちですが、プロは「制約条件(ConstraintType)がどうなっているか」を監視します。なぜなら、制約が変われば、タスクの開始日は「計算の結果」として勝手に動くからです。

2. システム設計:なぜ SQL Server なのか?

「変更履歴なんて、Excelやテキストファイルに保存すれば十分じゃないですか?」

そう思うかもしれません。しかし、大規模プロジェクトの監査ログには、以下の3つの要件が求められます。

1. 整合性: 誰かがログファイルを誤って消したり、書き換えたりしてはいけない。
2. 同時実行性: 複数のプロジェクトマネージャーが同時にログを書き込んでも、データが壊れないこと。
3. 分析性: 「先月、制約変更が多かったタスクはどれか?」といった集計を高速に行うこと。

これらを完璧に満たすのが、SQL Server です。VBAから ADO (ActiveX Data Objects) という技術を使って、SQL Serverへ直接データを放り込む仕組みを作ります。

3. 実装ステップ:監査ログシステムの構築

それでは、実際に手を動かしていきましょう。

ステップ1:SQL Server 側の準備

まず、受け皿となるテーブルをSQL Server側に作成しておきます。

-- 監査ログ保存用テーブルの作成
CREATE TABLE ProjectAuditLogs (
    LogID INT IDENTITY(1,1) PRIMARY KEY,
    ProjectName NVARCHAR(255),
    TaskName NVARCHAR(255),
    TaskID INT,
    OldConstraint NVARCHAR(50),
    NewConstraint NVARCHAR(50),
    ChangedBy NVARCHAR(100),
    ChangedAt DATETIME DEFAULT GETDATE()
);

ステップ2:VBAによる接続と書き込みロジック

ここからが本番です。VBAからSQL Serverへ接続し、データをインサートするコードを書きます。

【プロの知恵】
ここで重要なのは、「接続(Connection)のライフサイクル」です。毎回接続を開いて閉じるのは、大規模データではパフォーマンスの重みになりますが、監査ログのような「イベント駆動」の場合は、確実に書き込んでから閉じる「確実性」を優先します。

以下のコードを標準モジュールに貼り付けてください。

Option Explicit

' --- 定数定義 (SQL Serverへの接続情報) ---
Private Const SQL_SERVER_ADDR As String = "Your_Server_Name" ' サーバー名を入力
Private Const DB_NAME As String = "Your_Database_Name"      ' データベース名を入力

''' <summary>
''' SQL Serverへ監査ログを書き込むメインルーチン
''' </summary>
''' <param name="tName">タスク名</param>
''' <param name="tID">タスクID</param>
''' <param name="oldCons">変更前の制約</param>
''' <param name="newCons">変更後の制約</param>
Public Sub WriteAuditLog(tName As String, tID As Long, oldCons As String, newCons As String)
    Dim conn As Object
    Dim strConn As String
    Dim sql As String
    Dim userName As String
    
    ' 現在のユーザー名を取得
    userName = Environ("USERNAME")
    
    ' ADO Connectionオブジェクトの生成 (参照設定なしで動くLate Binding方式)
    Set conn = CreateObject("ADODB.Connection")
    
    ' 接続文字列の構築 (Windows認証を使用する場合)
    strConn = "Provider=SQLOLEDB;Data Source=" & SQL_SERVER_ADDR & _
              ";Initial Catalog=" & DB_NAME & ";Integrated Security=SSPI;"
    
    On Error GoTo ErrorHandler
    
    ' 接続オープン
    conn.Open strConn
    
    ' SQL文の組み立て (SQLインジェクション対策として本来はCommandオブジェクト推奨ですが、今回は分かりやすさ優先)
    ' プロジェクト名も取得して記録
    sql = "INSERT INTO ProjectAuditLogs (ProjectName, TaskName, TaskID, OldConstraint, NewConstraint, ChangedBy, ChangedAt) " & _
          "VALUES ('" & ActiveProject.Name & "', '" & tName & "', " & tID & ", '" & oldCons & "', '" & newCons & "', '" & userName & "', GETDATE());"
    
    ' SQL実行
    conn.Execute sql
    
    ' 接続を閉じる
    conn.Close
    Set conn = Nothing
    
    Debug.Print "Log written successfully: " & tName
    Exit Sub

ErrorHandler:
    MsgBox "監査ログの書き込みに失敗しました。" & vbCrLf & "Error: " & Err.Description, vbCritical
    If Not conn Is Nothing Then
        If conn.State = 1 Then conn.Close
    End Set conn = Nothing
End Sub

''' <summary>
''' 制約タイプを人間が読める文字列に変換するヘルパー関数
''' </summary>
Public Function GetConstraintName(consType As Long) As String
    Select Case consType
        Case 0: GetConstraintName = "As Soon As Possible"
        Case 1: GetConstraintName = "As Late As Possible"
        Case 2: GetConstraintName = "Start No Earlier Than"
        Case 3: GetConstraintName = "Start No Later Than"
        Case 4: GetConstraintName = "Must Start On"
        Case 5: GetConstraintName = "Start No Earlier Than" ' 重複注意
        Case 6: GetConstraintName = "Must Finish On"
        ' ... 必要に応じて追加
        Case Else: GetConstraintName = "Unknown"
    End Select
End Function

ステップ3:変更を検知するロジック(スナップショット比較法)

MS Project VBAには「制約が変わった瞬間に動くイベント」が実は非常に弱いです。そこで、プロは「現在の状態」と「前回の状態(スナップショット)」を比較するというアプローチを取ります。

''' <summary>
''' タスクの制約変更をチェックし、変更があればログを残す
''' </summary>
Public Sub CheckConstraintChanges()
    Dim t As Task
    Dim currentCons As Long
    Dim prevCons As Long ' 本来は前回保存した値をどこか(一時テーブル等)から持ってくる
    
    ' ※今回はデモ用に、特定のタスクの制約が「ASAP」から「Must Start On」に変わったと仮定して実行します
    ' 実運用では、タスクの属性をループで回し、DBに保存済みの値と比較します
    
    For Each t In ActiveProject.Tasks
        If Not t Is Nothing Then
            ' ここでは例として、強制的に制約が変わったと仮定したロジックを示します
            ' 実際には、DBから取得した「前回の制約値」と比較してください
            
            ' 仮の比較ロジック(実際はDBとの照合が必要)
            ' If t.ConstraintType <> PreviousValueFromDB Then
            '    Call WriteAuditLog(t.Name, t.ID, GetConstraintName(prev), GetConstraintName(t.ConstraintType))
            ' End If
        End If
    Next t
End Sub

4. 陥りやすいエラーと、プロの回避策

このシステムを運用する上で、初心者が必ずと言っていいほどハマるポイントが2つあります。

① 接続エラー(ネットワーク・権限)

SQL Serverへの接続は、ネットワークの状態やWindowsの権限に強く依存します。
対策: `On Error GoTo` を必ず使い、エラーが発生してもMS Project自体がクラッシュ(強制終了)しないように、エラーハンドリングを徹底してください。

② パフォーマンスの壁(ループの罠)

1万タスクあるプロジェクトで、タスクごとに `conn.Open` と `conn.Close` を繰り返すと、処理が劇的に遅くなります。
対策:

  • 一括処理: 変更があったタスクを一旦メモリ上の配列(CollectionやDictionary)に溜め込み、最後に一括でSQLを実行する。
  • 接続の維持: 処理の間だけ接続を開きっぱなしにする。

5. まとめ:ここをクリアすれば、あなたはもう「自動化エンジニア」です

今回の内容は、単なる「マクロの書き方」ではありません。
「データの信頼性をどう担保するか」という、システム設計の本質に触れる内容でした。

1. オブジェクトの理解: `ConstraintType` という、スケジュールの心臓部を捉える。
2. 外部連携: SQL Server という、エンタープライズの標準基盤へデータを飛ばす。
3. 堅牢な設計: エラーハンドリングと、パフォーマンスを意識したライフサイクル管理。

これらを意識できるようになれば、あなたの書くコードは「個人の便利ツール」から「企業の資産となるシステム」へと昇華します。

一歩ずつ、確実に。
技術の深淵を楽しんでください。応援していますよ!

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