Excel VBA 逆引き集 | 月次集計

Excel VBA
スポンサーリンク

月次集計

毎月の売上・件数・平均を「年月ごと」にまとめる定番テンプレを、初心者でも壊さず使える形で整理しました。少量なら関数が最短、大量なら配列+辞書が爆速、ピボットなら一発です。運用の現場に合わせて選べます。


選び方の指針

  • 最短・少量: SUMIFS/COUNTIFS/AVERAGEIFS を「当月範囲」で回す(年月の境界で安定)。
  • 大量・柔軟: 範囲→配列→辞書で「yyyy-mm」キー集計(高速・頑丈)。
  • 見た目で確認しながら: AutoFilterで月範囲抽出→Sum/Count。
  • 一発で表にしたい: ピボットテーブルで「年月×カテゴリー」集計。

基本:SUMIFS/COUNTIFS/AVERAGEIFSで月別集計(当月のみ抽出)

Sub MonthlySummary_Functions()
    '明細:A=日付(時刻付きOK), B=商品, C=金額
    Dim dateR As Range, amtR As Range
    Set dateR = Range("A2:A100000")
    Set amtR  = Range("C2:C100000")

    '集計側:F列に「月初日」が並んでいる(例:2025/01/01, 2025/02/01…)
    Dim outLast As Long: outLast = Cells(Rows.Count, "F").End(xlUp).Row
    Dim r As Long, d0 As Date, d1 As Date

    For r = 2 To outLast
        d0 = Range("F" & r).Value              '月初
        d1 = DateAdd("m", 1, d0)               '翌月初(上限)

        '合計(当月のみ)
        Range("G" & r).Value = Application.WorksheetFunction.SumIfs( _
                                amtR, dateR, ">=" & CLng(d0), dateR, "<" & CLng(d1))
        '件数
        Range("H" & r).Value = Application.WorksheetFunction.CountIfs( _
                                dateR, ">=" & CLng(d0), dateR, "<" & CLng(d1))
        '平均(0件対策はOn Errorでガード)
        On Error Resume Next
        Range("I" & r).Value = Application.WorksheetFunction.AverageIfs( _
                                amtR, dateR, ">=" & CLng(d0), dateR, "<" & CLng(d1))
        On Error GoTo 0
    Next
End Sub
VB
  • ポイント: 月の範囲は「月初以上かつ翌月初未満」で切ると、時刻付きデータでも確実に当月だけを集計できる。月リストがあれば任意区間も簡単に回せる。

爆速:配列+辞書で「yyyy-mm」キーに月次集計

Sub MonthlySummary_Dictionary()
    '明細:A=日付(時刻付きOK), C=金額
    Dim rg As Range: Set rg = Range("A1").CurrentRegion
    Dim v As Variant: v = rg.Value

    Dim sumMap As Object: Set sumMap = CreateObject("Scripting.Dictionary")
    Dim cntMap As Object: Set cntMap = CreateObject("Scripting.Dictionary")

    Dim i As Long, ym As String, amt As Double
    For i = 2 To UBound(v, 1)
        ym = Format$(v(i, 1), "yyyy-mm") '年月キー(時刻付きでもOK)
        amt = Val(v(i, 3))
        If sumMap.Exists(ym) Then
            sumMap(ym) = sumMap(ym) + amt
            cntMap(ym) = cntMap(ym) + 1
        Else
            sumMap.Add ym, amt
            cntMap.Add ym, 1
        End If
    Next

    '出力:F=年月、G=合計、H=件数、I=平均
    Dim keys As Variant: keys = sumMap.Keys
    If UBound(keys) >= 0 Then
        Dim iOut As Long, out() As Variant
        ReDim out(1 To UBound(keys) + 1, 1 To 4)
        For iOut = 0 To UBound(keys)
            out(iOut + 1, 1) = keys(iOut)
            out(iOut + 1, 2) = sumMap(keys(iOut))
            out(iOut + 1, 3) = cntMap(keys(iOut))
            out(iOut + 1, 4) = sumMap(keys(iOut)) / cntMap(keys(iOut))
        Next
        With Worksheets("月次集計")
            .Range("F1:I1").Value = Array("年月", "合計", "件数", "平均")
            .Range("F2").Resize(UBound(out, 1), UBound(out, 2)).Value = out
        End With
    End If
End Sub
VB
  • ポイント: 10万行規模でも、配列→辞書の組み合わせは非常に速い。年月キーは Format$(日付,"yyyy-mm") が安全。グループ軸を増やす場合はキーに「|」で連結すれば拡張できる。

ピボットテーブルで月次集計を一発作成・更新

Sub MonthlySummary_Pivot()
    Dim src As Range: Set src = Range("A1").CurrentRegion 'A=日付, B=商品, C=金額
    Dim pc As PivotCache, pt As PivotTable
    Set pc = ActiveWorkbook.PivotCaches.Create(xlDatabase, src)
    Set pt = pc.CreatePivotTable(TableDestination:=Worksheets("月次ピボット").Range("A3"), TableName:="月次PT")

    With pt
        .PivotFields("日付").Orientation = xlRowField
        .PivotFields("日付").NumberFormat = "yyyy-mm" '年月表示(グループでも可)
        .PivotFields("金額").Orientation = xlDataField
        .PivotFields("金額").Function = xlSum
        .PivotFields("金額").NumberFormat = "#,##0"
        '必要なら商品を列フィールドに
        '.PivotFields("商品").Orientation = xlColumnField
    End With
End Sub
VB
  • ポイント: ピボットなら「年月×カテゴリ」の表を瞬時に作れる。更新はピボットキャッシュの Refresh だけで済むため、定期運用と相性が良い。

見出し名で列特定→月次集計(列順変更に強い)

Sub MonthlySummary_ByHeaders()
    Dim rg As Range: Set rg = Range("A1").CurrentRegion
    Dim head As Range: Set head = rg.Rows(1)

    Dim cDate As Long: cDate = FindHeader(head, "日付")
    Dim cAmt  As Long: cAmt  = FindHeader(head, "金額")
    If cDate * cAmt = 0 Then MsgBox "見出しが見つかりません": Exit Sub

    Dim v As Variant: v = rg.Value
    Dim sumMap As Object: Set sumMap = CreateObject("Scripting.Dictionary")
    Dim cntMap As Object: Set cntMap = CreateObject("Scripting.Dictionary")

    Dim i As Long, ym As String
    For i = 2 To UBound(v, 1)
        ym = Format$(v(i, cDate), "yyyy-mm")
        If sumMap.Exists(ym) Then
            sumMap(ym) = sumMap(ym) + Val(v(i, cAmt))
            cntMap(ym) = cntMap(ym) + 1
        Else
            sumMap.Add ym, Val(v(i, cAmt))
            cntMap.Add ym, 1
        End If
    Next

    Dim k As Variant, rOut As Long: rOut = 2
    With Worksheets("月次集計")
        .Range("F1:I1").Value = Array("年月", "合計", "件数", "平均")
        For Each k In sumMap.Keys
            .Cells(rOut, "F").Value = k
            .Cells(rOut, "G").Value = sumMap(k)
            .Cells(rOut, "H").Value = cntMap(k)
            .Cells(rOut, "I").Value = sumMap(k) / cntMap(k)
            rOut = rOut + 1
        Next
    End With
End Sub

Private Function FindHeader(ByVal headerRow As Range, ByVal name As String) As Long
    Dim hit As Range
    Set hit = headerRow.Find(What:=name, LookAt:=xlWhole, LookIn:=xlValues, MatchCase:=False)
    FindHeader = IIf(hit Is Nothing, 0, hit.Column)
End Function
VB
  • ポイント: 列名で列番号を動的取得すれば、運用中の列順変更でも壊れない。辞書集計は縦持ちデータに相性が良い。

フィルタで「当月のみ」見せて合計・件数(最短)

Sub MonthlySummary_FilterThenSum()
    With Range("A1").CurrentRegion
        '例:2025年1月(1/1以上、2/1未満)
        .AutoFilter Field:=1, Operator:=xlAnd, Criteria1:=">=1/1/2025", Criteria2:="<2/1/2025"

        Dim vis As Range, sumAmt As Double, cnt As Long
        On Error Resume Next
        Set vis = .Columns(3).SpecialCells(xlCellTypeVisible) '金額列(C列)
        On Error GoTo 0
        If Not vis Is Nothing Then
            sumAmt = Application.WorksheetFunction.Sum(vis)
            cnt = .Offset(1, 0).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Rows.Count
        End If

        Range("G2").Value = sumAmt
        Range("H2").Value = cnt
        Range("I2").Value = IIf(cnt > 0, sumAmt / cnt, 0)
        .AutoFilter
    End With
End Sub
VB
  • ポイント: 目視で確認しながら結果も欲しいときに便利。解除忘れに注意。

大量データの安全・高速ラップ

Sub SafeWrapStart()
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.Calculation = xlCalculationManual
End Sub

Sub SafeWrapEnd()
    Application.Calculation = xlCalculationAutomatic
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub
VB
  • ポイント: 前後で停止→復帰を挟むだけで体感速度が改善。エラー時も必ず復帰させる。

よくある落とし穴と対策

  • 日時付きで月が崩れる: Date型のまま比較すると境界で漏れが出る。月初以上かつ翌月初未満で切るか、キーは Format$(date,"yyyy-mm") に丸める。
  • 列順変更で壊れる: 見出し名で列探索して耐久性を上げる(FindHeader)。
  • OR条件(複数カテゴリ)を月次で集約したい: 辞書のキーに「年月|カテゴリ」を連結すれば一発で軸追加。
  • セル往復で遅い: 範囲→配列→辞書→一括書き戻しが鉄板。ピボットも有効。

例題で練習

'例1:月初リスト(F列)に対してSUMIFS/COUNTIFS/AVERAGEIFSで月次指標
Sub Example_FunctionsMonthly()
    Call MonthlySummary_Functions
End Sub

'例2:辞書で「yyyy-mm」キーの月次合計・件数・平均を一括出力
Sub Example_DictMonthly()
    Call MonthlySummary_Dictionary
End Sub

'例3:見出し名で列を見つけて月次集計(列順変更に強い)
Sub Example_ByHeadersMonthly()
    Call MonthlySummary_ByHeaders
End Sub

'例4:ピボットテーブルで月次集計を作成・更新
Sub Example_PivotMonthly()
    Call MonthlySummary_Pivot
End Sub
VB
タイトルとURLをコピーしました