Excel VBAでエラーメッセージの発生回数を集計する方法

VBAでerror messageを集計で最初に求められるのはcommand暗記ではなく、何を正しい結果とするかの定義です。結論は「source列を最後の行まで読み、blankを除外し、Scripting.Dictionaryでmessageごとの件数を数えます。出力sheetを毎回上書きする前に作業copyを使い、元messageと正規化keyを両方残します。」。raw logを変更せず、messageの正規化ruleとblank/error cellの扱いを固定することを確認し、表示、加工、変更を混同しない順序で進めます。 確認ポイント:集計結果はmessage正規化とExcel error cellの採否を同じ規則で再現できる場合にだけ比較できます。

目次

集計対象列と正規化規則を先に決める

エラーメッセージ集計では、入力列の範囲、空白、数式エラー、前後空白、大文字小文字の扱いを最初に決めます。元ブックを複製し、Dictionaryへ入れる正規化後の文字列と原文を分けて保持します。集計表の件数合計が有効入力件数と一致し、同じメッセージが一行だけ出ることを確認します。

  • source sheet、header、最終行、message列を確認する
  • blank、Excel error値、数字、改行を別に扱う
  • 大小文字、前後空白、可変IDを同一化するかruleを決める
  • 出力sheetの既存dataとsort範囲を確認する

Dictionary集計から出力sheetまでを組み立てる

Log sheetの最終行を確認する

Sub InspectErrors()
  Dim ws As Worksheet, lastRow As Long
  Set ws = ThisWorkbook.Worksheets("Log")
  lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
  Debug.Print lastRow, ws.Range("A2").Value2
End Sub

固定A1:A100ではなくheader下から最終行を確認します。

大文字小文字を無視してmessageを数える

Sub CountMessages()
  Dim d As Object, c As Range, key As String
  Set d = CreateObject("Scripting.Dictionary")
  d.CompareMode = vbTextCompare
  For Each c In ThisWorkbook.Worksheets("Log").Range("A2:A1000")
    If Not IsError(c.Value) Then
      key = Trim$(CStr(c.Value2))
      If Len(key) > 0 Then d(key) = d(key) + 1
    End If
  Next c
  Debug.Print d.Count
End Sub

実rangeは確認したlastRowへ置き換えます。

ErrorSummaryへkeyと件数を出力する

Dim outWs As Worksheet, i As Long, k As Variant
Set outWs = ThisWorkbook.Worksheets("ErrorSummary")
i = 2
For Each k In d.Keys
  outWs.Cells(i,1).Value2 = k
  outWs.Cells(i,2).Value2 = d(k)
  i=i+1
Next k

既存summaryを消す前にbackupし、headerを明示します。

件数列で降順sortする

With outWs.Sort
  .SortFields.Clear
  .SortFields.Add Key:=outWs.Range("B2:B" & i-1), Order:=xlDescending
  .SetRange outWs.Range("A1:B" & i-1)
  .Header = xlYes
  .Apply
End With

ActiveSheetのRangeを混ぜません。

採用件数とsummary合計を照合する

Debug.Assert WorksheetFunction.Sum(outWs.Range("B2:B" & i-1)) = WorksheetFunction.CountA(ThisWorkbook.Worksheets("Log").Range("A2:A1000"))

blankやerror cellを除外する場合は期待件数も同じruleで計算します。

空白・Excel error値・表記揺れを除外する

messageの完全一致集計はtimestampやIDが埋め込まれると細分化されます。正規化する場合は原文を保持し、どのpatternを置換したか記録します。vbTextCompareは大小文字を同一視しますが、日本語全角半角やUnicode正規化は別です。Excel error cellはCStrするとerrorになるためIsErrorで分けます。

部分一致と完全一致の集計を混同しない

  • blankも一つのmessageとして数える
  • Error cellでCStrする
  • ActiveSheetに出力する
  • sort rangeをqualifyしない
  • 正規化後に原文を失う

出力sheet以外を消去しない停止条件

集計はsourceを読み取るだけにし、色付けやfilterは出力sheetへ限定します。messageにuser名やticket本文がある場合はsummaryの共有範囲を確認します。macro実行前にworkbook copyを作り、Application.Calculation/ScreenUpdating/EnableEventsを変えた場合はerror handlerで元へ戻します。誤出力時はsummary sheetだけをbackupから戻します。

accepted・rejected・key数を証跡にする

source非blank件数、dictionary count合計、出力count合計を一致させます。大小文字、空白、改行、Excel error、同文反復のsampleをtestし、二回実行して重複追記されないことを確認します。

エラーメッセージ集計の合格基準

Log sheetの最終行までをScripting.Dictionaryで集計し、blankとExcel error値を除外してErrorSummaryへ出します。dictionary、出力sheet、sort範囲を同じSubのlocal変数として保持し、断片を別々に実行して未定義変数へ依存しません。

一つのSubで完結する集計コード

Option Explicit

Public Sub BuildErrorSummary()
  Dim src As Worksheet, dst As Worksheet
  Dim lastRow As Long, r As Long, outRow As Long
  Dim d As Object, key As String, k As Variant
  Dim accepted As Long, rejected As Long
  On Error GoTo Fail

  Set src = ThisWorkbook.Worksheets("Log")
  Set dst = ThisWorkbook.Worksheets("ErrorSummary")
  Set d = CreateObject("Scripting.Dictionary")
  d.CompareMode = vbTextCompare
  lastRow = src.Cells(src.Rows.Count, "A").End(xlUp).Row

  For r = 2 To lastRow
    If IsError(src.Cells(r, "A").Value) Then
      rejected = rejected + 1
    Else
      key = Trim$(CStr(src.Cells(r, "A").Value2))
      If Len(key) > 0 Then
        If d.Exists(key) Then d(key) = CLng(d(key)) + 1 Else d.Add key, 1
        accepted = accepted + 1
      End If
    End If
  Next r

  dst.Cells.ClearContents
  dst.Cells(1, 1).Value2 = "Message"
  dst.Cells(1, 2).Value2 = "Count"
  outRow = 2
  For Each k In d.Keys
    dst.Cells(outRow, 1).Value2 = CStr(k)
    dst.Cells(outRow, 2).Value2 = CLng(d(k))
    outRow = outRow + 1
  Next k

  If outRow > 2 Then
    With dst.Sort
      .SortFields.Clear
      .SortFields.Add Key:=dst.Range("B2:B" & outRow - 1), Order:=xlDescending
      .SetRange dst.Range("A1:B" & outRow - 1)
      .Header = xlYes
      .Apply
    End With
    Debug.Assert CLng(WorksheetFunction.Sum(dst.Range("B2:B" & outRow - 1))) = accepted
  Else
    Debug.Assert accepted = 0
  End If
  Debug.Print "accepted=" & accepted, "rejectedErrorCells=" & rejected, "keys=" & d.Count
  Exit Sub
Fail:
  MsgBox "BuildErrorSummary failed: " & Err.Number & " " & Err.Description, vbExclamation
End Sub

このcodeは画面表示だけを見るための例ではありません。通常系では「非blank・非errorの入力件数とsummary Count列合計が一致し、message別件数が正しい」を確認し、0件と実行errorを別の結果として保存します。

正常message・空白・cell errorを分類する

  • 期待どおり:非blank・非errorの入力件数とsummary Count列合計が一致し、message別件数が正しい
  • 0件・非適用:有効message 0件ならheaderだけを残してNoRowsとして正常終了する
  • 実行error:sheet/table不存在、出力保護、sort errorはErr.Numberを表示し、途中summaryを完成扱いしない

固定A2:A1000で末尾を切らず、lastRowを取得します。大文字小文字を同一視するかはCompareModeで固定し、正規化後のkeyと原文sampleを必要に応じて分けます。既存summaryはcopyで保全してからclearします。

case差・空範囲・同数順位を試す

' Log!A2:A6 に Err A / err a / blank / CVErr(xlErrNA) / Err B を置き、
' summary合計=3、vbTextCompare時のkey数=2を確認する。

blank、Excel error、大小文字、前後空白、1000行超、0件をtestします。scanned行数、accepted/rejected件数、dictionary key数、summary合計、二回実行後の重複なしを記録します。

Excel VBAでエラーメッセージの発生回数を集計する方法の証跡には、実行対象と取得時刻に加え、通常・0件・errorのどれへ分類したかを残します。通常系は「非blank・非errorの入力件数とsummary Count列合計が一致し、message別件数が正しい」、停止系は「sheet/table不存在、出力保護、sort errorはErr.Numberを表示し、途中summaryを完成扱いしない」を判断文としてそのまま作業票へ写し、担当者ごとの言い換えで意味が変わらないようにします。

error集計macroを反復実行する場合は、固定行数ではなくtableまたは採用した最終行をrun開始時に確定し、集計先を新しい作業sheetへ出します。既存summaryへの追記は行わず、run IDごとに置換して再実行の二重計上を防ぎます。集計総数が読取行数から除外行数を引いた値と一致しなければ成果物を採用せず、CalculationとScreenUpdatingを元へ戻して終了します。

公式情報・参考資料

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次