【実務・中級編】【中級者向け】ページ削除時に「関連する外部DBデータ」もクリーンアップする連動型削除ツール – Visio VBA解析バイブル

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

Visio VBAを超えろ:ページ削除と外部DBの整合性を守る「アトミック・デリート」の設計思想

「ページを削除した。よし、完了だ。」

もし君が、Visioのページを `Page.Delete` メソッドで消去しただけで満足しているなら、今すぐその思考を捨ててほしい。君が作っているのは「便利なツール」ではない。データの整合性を破壊し、将来的にデータベースに「存在しないはずの参照」を撒き散らす時限爆弾だ。

実務レベルのシステムにおいて、Visioは単なる描画ツールではない。それは「データモデルの可視化レイヤー」だ。ページが特定のSQL Serverのレコード(例えば、設備管理IDやプロジェクトID)と紐付いている場合、Visio側のオブジェクト削除と、DB側のレコード更新は、「不可分な一つの操作(Atomic Operation)」として完結しなければならない。

今回は、中級者への階段を登るために、Visio VBAからSQL Serverを制御し、整合性を保ちながらクリーンアップを行う「連動型削除ツール」の極意を伝授する。

1. 破綻する設計、堅牢な設計

なぜ、単純なコードではいけないのか。設計の差は「例外(エラー)」が起きた瞬間に露呈する。

非効率・危険な設計(スクリプト・レベル)

1. Visioのページを削除する。
2. SQLの `DELETE` または `UPDATE` 文を実行する。

これの何が問題か?
もし、2のDB操作中にネットワークが切断されたらどうなる? Visioのページは消えたが、DBにはゴミデータが残る。これが「孤立したレコード(Orphaned Record)」だ。一度増え始めたゴミデータは、システム全体の信頼性を蝕む。

堅牢な設計(エンジニアリング・レベル)

プロフェッショナルは、「データベースの状態を先に確定させ、Visioの操作をその結果に従わせる」、あるいは「トランザクションによるロールバックを考慮する」設計をとる。

1. 検証フェーズ: 削除対象のページに、DB紐付け用のプロパティ(例:`RecordID`)が存在するか確認する。
2. DBトランザクション開始: SQL Server側で更新処理を開始する。
3. DB更新(ソフトデリート推奨): レコードを物理削除するのではなく、`IsDeleted = 1` のようなフラグ更新を行う。
4. Visio操作: ページを削除する。
5. 確定フェーズ: Visioの削除が成功したら、DBのトランザクションを `COMMIT` する。失敗したら `ROLLBACK` する。

2. 実装の鍵:ページとDBを繋ぐ「隠れたメタデータ」

ページとDBをどう紐付けるか? 図形(Shape)の中にIDを持たせるのは初歩的すぎる。ページそのものに、Custom Properties(ユーザー定義プロパティ)を埋め込むのが、最もスマートで保守性の高い手法だ。

Visioの `Page.CustomProperties` を利用することで、描画要素とは独立した、純粋な「メタデータ・レイヤー」を構築できる。

3. プロダクション・コード例

以下に、ADODBを用いた堅牢な連動削除のテンプレートを示す。これは単なるコピペ用ではない。各行に「なぜその処理が必要か」という意図を込めている。

Option Explicit

' --- 構成定数 ---
Private Const DB_CONN_STR As String = "Provider=SQLOLEDB;Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;User ID=YOUR_USER;Password=YOUR_PASSWORD;"
Private Const PROP_NAME_ID As String = "DB_Record_ID" ' ページに埋め込むプロパティ名

''' <summary>
''' ページとDBの整合性を保ちながらページを削除するメインルーチン
''' </summary>
''' <param name="pg">削除対象のVisioページ</param>
Public Sub SafeDeletePageWithDB(ByRef pg As Visio.Page)
    Dim recordID As String
    Dim conn As ADODB.Connection
    Dim cmd As ADODB.Command
    Dim success As Boolean
    
    On Error GoTo ErrorHandler

    ' 1. メタデータの抽出
    ' ページに紐付くDB IDを取得。存在しない場合はエラーとして扱う。
    recordID = GetPageCustomProperty(pg, PROP_NAME_ID)
    If recordID = "" Then
        MsgBox "エラー: このページにはDB紐付けIDが設定されていません。削除を中止します。", vbCritical
        Exit Sub
    End If

    ' 2. ユーザーへの最終確認
    If MsgBox("ページおよび関連DBレコード(ID: " & recordID & ")を削除しますか?", vbQuestion + vbYesNo) <> vbYes Then
        Exit Sub
    End If

    ' 3. DB接続とトランザクションの準備
    Set conn = New ADODB.Connection
    conn.ConnectionString = DB_CONN_STR
    conn.Open
    
    ' トランザクション開始
    conn.BeginTrans
    
    ' 4. DB側の更新(ソフトデリートによる整合性確保)
    ' 物理削除(DELETE)ではなく、フラグ更新(UPDATE)を推奨。
    ' 万が一Visio側で失敗した際、後から復旧が可能になるため。
    Set cmd = New ADODB.Command
    With cmd
        .ActiveConnection = conn
        .CommandText = "UPDATE YourTable SET IsDeleted = 1, DeletedAt = GETDATE() WHERE RecordID = ?"
        .Parameters.Append .CreateParameter("@ID", adVarChar, adParamInput, 50, recordID)
        .Execute
    End With

    ' 5. Visio側の操作
    ' DB更新が成功した後に、Visioのオブジェクトを操作する
    pg.Delete
    
    ' 6. 全て成功した場合のみ、DBの変更を確定させる
    conn.CommitTrans
    success = True
    MsgBox "ページおよびDBレコードのクリーンアップが完了しました。", vbInformation

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

ErrorHandler:
    ' エラーが発生した場合は、DBの状態を元の状態にロールバックする
    If Not conn Is Nothing Then
        If conn.State = adStateOpen Then
            conn.RollbackTrans
            MsgBox "DB更新中にエラーが発生したため、ロールバックしました。" & vbCrLf & _
                   "エラー内容: " & Err.Description, vbCritical
        End If
    End If
    
    ' Visio側の操作でエラーが起きた場合、DBはロールバックされているため整合性は保たれる
    Resume CleanUp
End Sub

''' <summary>
''' ページから特定のカスタムプロパティの値を取得するヘルパー関数
''' </summary>
Private Function GetPageCustomProperty(ByRef pg As Visio.Page, ByVal propName As String) As String
    Dim cp As Visio.CustomProperty
    
    On Error Resume Next
    For Each cp In pg.CustomProperties
        If cp.Name = propName Then
            GetPageCustomProperty = CStr(cp.Value)
            Exit Function
        End If
    Next cp
    On Error GoTo 0
    
    GetPageCustomProperty = "" ' 見つからない場合は空文字を返す
End Function

4. アーキテクトからの警告:運用上の注意点

このコードをプロダクション環境に投入する前に、以下の3点を必ず検討せよ。

① 「物理削除」か「論理削除」か

上記のコードでは `UPDATE … SET IsDeleted = 1` という論理削除(Soft Delete)を採用している。これはエンジニアリングにおける鉄則だ。Visio側の操作(例えば、大量のページを一括削除するループ処理など)で予期せぬエラーが起きた際、DB側を物理的に消してしまっていると、復旧が不可能になる。DB側には必ず「いつ、誰が消したか」の証跡を残せ。

② ページ削除のループ処理における罠

複数のページを一度に削除するツールを作る場合、`For Each pg In ActiveDocument.Pages` のようなループをそのまま使うな。ページを削除していくと、コレクションのインデックスがズレて、処理がスキップされたりエラーになったりする。
必ず 「後ろのページから(Step -1)」 または 「削除対象を一度配列に格納してから」 処理を行うこと。

③ エラーハンドリングの深度

VBAの `On Error GoTo` は強力だが、万能ではない。特にADODBの接続エラーやタイムアウトは、ネットワーク環境に依存する。本番環境では、接続文字列に `Connect Timeout=30` を含めるなど、インフラ側の挙動をコード側でコントロールする意識を持て。

結論

Visio VBAで業務ツールを構築する際、君の戦場はVisioのキャンバス内だけではない。その背後にあるデータベース、そしてデータ全体の整合性が、君の真の戦場だ。

「動けばいい」という思考を捨て、「壊れない設計」に時間を割け。それが、単なるスクリプト作成者と、信頼されるアーキテクトを分かつ境界線である。

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