【実務・中級編】【上級者向け】SQL Server連携:プロジェクトの保存情報をデータベースへ自動記録する – Project VBA解析バイブル

スポンサーリンク

プロローグ:ファイルという「情報の孤島」を、組織の「資産」へ昇華させる

プロジェクト管理の実務において、Microsoft Project(以下MS Project)の`.mpp`ファイルは、往々にして「情報の孤島」になりがちです。
各PMが個別にファイルを保存し、進捗を更新する。しかし、PMOや部門長が「今、全プロジェクトの最新ベースラインはどうなっているか?」「最後に誰がいつ更新したのか?」を知ろうとしたとき、散らばったファイルを一つずつ開くという、極めて非生産的な作業が発生します。

Project OnlineやProject Serverを導入すれば解決する問題かもしれません。しかし、コストや導入リードタイム、そして「そこまでの重厚なシステムは求めていない」という現場のリアリティがある。

ならば、VBAという「銀の弾丸」で、保存と同時にSQL Serverへメタデータを書き込む自動ログシステムを構築すればいい。

今回は、単なるリファレンスの切り貼りではない。私が大規模プロジェクトの自動化において徹底している、「堅牢性」「保守性」「パフォーマンス」を両立させたプロフェッショナルな実装法を伝授します。

1. データベース設計:ログは「証跡」である

まずは受け皿となるSQL Serverのテーブル設計です。ここで重要なのは、「いつ、誰が、どのファイルを、どのような状態で」保存したかを一意に特定できることです。

— プロジェクト保存ログテーブルの作成
CREATE TABLE ProjectSaveLogs (
LogID INT IDENTITY(1,1) PRIMARY KEY,
ProjectName NVARCHAR(255) NOT NULL,
FilePath NVARCHAR(500),
LastSavedBy NVARCHAR(100),
SaveTimestamp DATETIME DEFAULT GETDATE(),
— ベースライン情報(主要な指標のみ抽出)
BaselineDurationMinutes FLOAT,
BaselineCost MONEY,
BaselineWork FLOAT,
— 運用管理用
AppVersion NVARCHAR(50)
);

アーキテクトの視点:
なぜ`.mpp`ファイルそのものをバイナリでDBに入れないのか? それはDBの肥大化を招き、パフォーマンスを劣化させるからです。ファイルはファイルサーバーへ。DBには「検索と分析に必要なメタデータ」のみを抽出して格納する。 これが疎結合な設計の鉄則です。

2. 実装の要:Applicationレベルのイベントトラップ

MS ProjectのVBAで「保存時」を捉えるには、`ThisProject`モジュールにコードを書くのが一般的ですが、それはアマチュアの仕事です。複数のプロジェクトファイルを扱う場合、各ファイルにコードを埋め込むのは保守性の地獄を招きます。

プロフェッショナルは、クラスモジュールを用いた「Applicationイベントの捕捉」を選択します。

クラスモジュール:`clsProjectApp`

‘—————————————————————————————
‘ Class : clsProjectApp
‘ Purpose : MS Projectアプリケーションレベルのイベントを制御する
‘—————————————————————————————
Option Explicit

Private WithEvents pApp As MSProject.Application

Private Sub Class_Initialize()
Set pApp = MSProject.Application
End Sub

‘ プロジェクト保存前に発火するイベント
Private Sub pApp_ProjectBeforeSave(ByVal pj As Project, ByVal SaveAsUi As Boolean, Cancel As Boolean)
On Error GoTo ErrorHandler

‘ 保存処理の本体を呼び出し
Call modDatabase.RecordProjectMetadata(pj)

Exit Sub
ErrorHandler:
‘ 保存そのものを止めないよう、エラーはログ出力に留める(サイレント失敗の原則)
Debug.Print “Logging Error: ” & Err.Description
End Sub

3. SQL Server連携:ADODBによる堅牢なデータ転送

次に、実際にDBへ接続し、データを書き込むロジックを標準モジュールに記述します。
ここで絶対にやってはいけないのは、SQL文の文字列結合です。SQLインジェクション対策はもちろん、ファイル名に含まれるシングルクォーテーション等で構文エラーを起こさないよう、必ず`ADODB.Command`オブジェクトとパラメータを使用します。

標準モジュール:`modDatabase`

‘—————————————————————————————
‘ Module : modDatabase
‘ Purpose : SQL Serverへのデータ永続化ロジック
‘—————————————————————————————
Option Explicit

Public Sub RecordProjectMetadata(ByRef pj As MSProject.Project)
Dim conn As Object ‘ ADODB.Connection
Dim cmd As Object ‘ ADODB.Command
Dim connStr As String

‘ 1. 接続文字列の設定(環境に合わせて変更)
‘ ※ 実際には暗号化して保持するか、Windows認証を推奨
connStr = “Provider=SQLNCLI11;Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;Integrated Security=SSPI;”

On Error GoTo ConnError
Set conn = CreateObject(“ADODB.Connection”)
conn.Open connStr

‘ 2. パラメータ化クエリの構築
Dim sql As String
sql = “INSERT INTO ProjectSaveLogs (ProjectName, FilePath, LastSavedBy, ” & _
“BaselineDurationMinutes, BaselineCost, BaselineWork, AppVersion) ” & _
“VALUES (?, ?, ?, ?, ?, ?, ?)”

Set cmd = CreateObject(“ADODB.Command”)
With cmd
.ActiveConnection = conn
.CommandText = sql
.CommandType = 1 ‘ adCmdText

‘ パラメータのバインド(MS Projectのオブジェクトモデルから値を取得)
.Parameters.Append .CreateParameter(“@pjName”, 202, 1, 255, pj.Name) ‘ adVarWChar
.Parameters.Append .CreateParameter(“@path”, 202, 1, 500, pj.Path)
.Parameters.Append .CreateParameter(“@user”, 202, 1, 100, Environ(“USERNAME”))

‘ ベースライン設定の有無を確認し、存在しなければ0をセット
If pj.BaselineSaved(pjBaseline0) Then
.Parameters.Append .CreateParameter(“@dur”, 5, 1, , pj.BaselineDuration(pjBaseline0)) ‘ adDouble
.Parameters.Append .CreateParameter(“@cost”, 6, 1, , pj.BaselineCost(pjBaseline0)) ‘ adCurrency
.Parameters.Append .CreateParameter(“@work”, 5, 1, , pj.BaselineWork(pjBaseline0))
Else
.Parameters.Append .CreateParameter(“@dur”, 5, 1, , 0)
.Parameters.Append .CreateParameter(“@cost”, 6, 1, , 0)
.Parameters.Append .CreateParameter(“@work”, 5, 1, , 0)
End If

.Parameters.Append .CreateParameter(“@ver”, 202, 1, 50, Application.Version)

‘ 3. 実行
.Execute
End With

CleanUp:
If Not conn Is Nothing Then
If conn.State = 1 Then conn.Close
End If
Set cmd = Nothing
Set conn = Nothing
Exit Sub

ConnError:
‘ 業務を止めないよう、エラーはログに残して静かに終了
MsgBox “ベースライン情報のDB保存に失敗しました。管理者へ連絡してください。” & vbCrLf & _
Err.Description, vbCritical, “Database Error”
Resume CleanUp
End Sub

4. なぜこの設計が「上級者向け」なのか

① 非同期的な「失敗」への配慮

DB連携において最も恐れるべきは、「DBがダウンしているせいで、ユーザーがファイルを保存できなくなること」です。上記のコードでは、`ProjectBeforeSave`内でエラーハンドリングを完結させ、最悪DB接続に失敗しても`Cancel = True`を返さないようにしています。プロジェクトの遂行(保存)が最優先であり、ログはあくまで副次的なものであるという優先順位の設計です。

② パラメータ化クエリの徹底

`Project.Name`には「2023年度_【重要】プロジェクトA.mpp」のように、SQLの予約語や特殊文字が含まれる可能性が非常に高いです。文字列結合でSQLを組むのは、バグを招き入れる行為に他なりません。`ADODB.Command`による型定義は、堅牢なシステムの最低条件です。

③ ベースラインの動的判定

MS Projectにおいて、ベースラインが保存されていない状態で`pj.BaselineDuration`を参照するとエラーを吐くか、予期せぬ値を返します。`pj.BaselineSaved(pjBaseline0)`で存在チェックを行うのは、Project VBAを知り尽くした者だけが辿り着く「作法」です。

5. 運用への組み込み:Global.mptへの配置

このコードを全ての環境で有効にするには、個別のプロジェクトファイルではなく、各ユーザーの`Global.mpt`、あるいは組織で共有しているアドイン(`.mpp`テンプレート)に実装します。

`ThisProject`モジュールで、起動時にクラスをインスタンス化するコードを忘れずに記述してください。

‘ ThisProject モジュール
Private myAppEntity As clsProjectApp

Private Sub Project_Open(ByVal pj As Project)
Set myAppEntity = New clsProjectApp
End Sub

エピローグ:データが意志を持ち始める

このシステムが稼働し始めると、SQL Serverには「いつ、誰が、どの程度の規模(コスト・工数)のプロジェクトを動かしているか」の生データが蓄積されます。

これをPower BIで可視化すれば、「保存頻度が極端に低いプロジェクトは炎上リスクが高い」「ベースラインが頻繁に書き換えられているプロジェクトは計画が甘い」といった、経営層が喉から手が出るほど欲しがるインサイトを、VBA一本で提供できるようになるのです。

コードを書くことは手段に過ぎません。その先にある「ガバナンス」と「意思決定の迅速化」を設計すること。それが、我々チーフアーキテクトの使命です。

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