Project VBAを掌握せよ:SQL Server連携における「負けない」データ設計
業務自動化の現場で、Microsoft Project(MSP)と外部データベース(SQL Server)を接続しようとする際、多くのエンジニアが「なんとなく」のコードを書いて地獄を見る。接続が切れる、型が一致しない、あるいはProjectの計算エンジンと競合してデータが壊れる……。
今回は、Project VBAのアーキテクチャを理解し、SQL Serverと「堅牢に」対話するための極限の知見を授ける。
—
1. なぜ「直接操作」が危険なのか
SQL Serverからタスク情報を読み込む際、初心者は `Task.Name` や `Task.Start` に直接値を放り込もうとする。これは極めて危険だ。
- Projectは「計算エンジン」である: `Start` 日付を書き込んでも、依存関係(Predecessors)やカレンダー設定によって、MSPが勝手に値を再計算する。
- ライフサイクルの不一致: DB上のデータとProject上のデータは、同期タイミングを厳密に制御しないと、いわゆる「ゴーストデータ(消しても消えない矛盾した状態)」を生む。
アーキテクトの教訓: 「読み込み」は常にステージング領域を介せ。直接Projectオブジェクトを触るのは、バリデーションが完了した後の一瞬のみに限定するのだ。
—
2. ADOによる堅牢な接続設定
VBAでSQL Serverを扱うなら、`ADODB`が標準かつ最強だ。ただし、接続文字列の管理とエラーハンドリングを怠ってはならない。
‘ 必要なライブラリ: Microsoft ActiveX Data Objects x.x Library
Option Explicit
Private Function GetConnection() As ADODB.Connection
Dim conn As New ADODB.Connection
‘ 接続タイムアウトを明示的に設定(ネットワークの揺らぎを考慮)
conn.ConnectionTimeout = 15
‘ 認証は可能な限りWindows認証(SSPI)を推奨。パスワード直書きは悪だ。
conn.ConnectionString = “Provider=SQLOLEDB;Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;Integrated Security=SSPI;”
On Error Resume Next
conn.Open
If Err.Number <> 0 Then
Err.Raise vbObjectError + 1000, “DBConnect”, “SQL Serverへの接続に失敗しました。”
End If
On Error GoTo 0
Set GetConnection = conn
End Function
—
3. データ型マッピングの「落とし穴」
Projectのデータ型は独特だ。特に以下の3点に注意せよ。
1. Date型: Projectの内部日付は `Double`(シリアル値)で管理されている。SQL Serverの `DATETIME` とは厳密に変換が必要。
2. Duration型: 「1d」や「1w」といった期間データは、Project内部では「分」換算の数値(`Long`)で保持されている。SQL Server側には「分単位の数値」で格納するのが鉄則だ。
3. Nullの扱い: タスクの `Finish` が未確定(Null)の場合、VBA側で `Empty` を適切にハンドリングしなければ型不一致でクラッシュする。
—
4. 実装:SQL Serverからタスクを一括同期する
保守性を考慮し、オブジェクト指向的な構造を意識した「タスク読み込み」のプロダクションコード例を示す。
Public Sub SyncTasksFromSQL()
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim tsk As Task
Dim sql As String
Set conn = GetConnection()
‘ 必要なカラムのみを抽出する(SELECT は絶対禁止)
sql = “SELECT TaskID, TaskName, StartDate, DurationMinutes FROM ProjectTasks WHERE ProjectID = 101”
Set rs = conn.Execute(sql)
‘ プロジェクトの計算を一時停止(パフォーマンス向上の秘訣)
Application.Calculation = pjManual
Do Until rs.EOF
‘ 既存タスクを検索、なければ新規作成
Set tsk = ActiveProject.Tasks.Add
‘ 型変換を意識した代入
tsk.Name = rs!TaskName
tsk.Start = rs!StartDate
tsk.Duration = rs!DurationMinutes ‘ 分単位で直接代入可能
rs.MoveNext
Loop
‘ 計算を再開し、整合性を担保
Application.Calculation = pjAutomatic
‘ クリーンアップ
rs.Close
conn.Close
Set rs = Nothing
Set conn = Nothing
End Sub
—
5. 最後に:現場を救うための設計指針
このコードをそのままコピペして終わりにしてはならない。以下の3点を常に意識せよ。
1. トランザクション管理: 複数テーブルを更新する場合、ADOの `BeginTrans` / `CommitTrans` を使用すること。途中で落ちた場合、Projectのデータだけが更新されてDBと不一致になる惨事を防ぐためだ。
2. ロギング: どのタスクでインポートが失敗したか、`Err.Description` をテキストファイルに出力する仕組みを必ず作れ。
3. UIのロック: `Application.ScreenUpdating = False` を活用せよ。画面描画を止めるだけで、Projectの実行速度は劇的に向上する。
VBAは、正しく扱えば強力な武器だが、雑に扱えば負債となる。君が書くコードが、明日の現場の「救い」になることを願っている。健闘を祈る。
