ねらい:集計ロジックは「設定シート」に閉じ込めて、コードは一切いじらない
毎回「この列でグループ化して、この列を合計して…」という集計マクロを書き換えていくと、
集計パターンが増えるたびにコードが増え、誰も触れないマクロが量産されます。
ノーコード集計ツールのゴールはシンプルです。
どのシートを集計対象にするか、どの列でグループ化するか、どの列を合計・件数・最大値などにするか。
これをすべて「設定シート」に書くだけで、VBAのコードは一切触らずに集計が走るようにすることです。
コードは「汎用集計エンジン」として一度だけ作る。
集計内容は「設定シート」で差し替える。
この分離ができると、現場の人が自分で集計パターンを増やせるようになります。
全体設計:集計設定シート+汎用集計エンジン
シート構成のイメージ
最低限、次の三つを用意します。
集計対象データを置くシート(例:Data)
売上明細、アクセスログ、アンケート結果など、行明細が並ぶシートです。
集計結果を出力するシート(例:Agg)
グループごとの合計や件数などを出すシートです。
集計設定シート(例:ConfigAgg)
どのシートを、どの列で、どう集計するかを表形式で書くシートです。
VBA側は「ConfigAgg を読み、そこに書かれたルール通りに Data を集計して Agg に出す」汎用エンジンだけを持ちます。
集計内容を変えたいときは、ConfigAgg の内容だけを変えます。
集計設定シートの設計:何をどう書くか
1行=1つの集計ルールという形にする
ConfigAgg シートに、次のような列を用意します。
A列:有効フラグ(Y の行だけ処理)
B列:SourceSheet(集計元シート名)
C列:TargetSheet(集計結果シート名)
D列:GroupCols(グループ化に使う列。カンマ区切り)
E列:ValueCol(集計対象の値列)
F列:AggFunc(集計関数名:SUM, COUNT, MAX, MIN など)
G列:OutStartCol(出力開始列)
イメージを表にするとこうなります。
| A | B | C | D | E | F | G |
|---|---|---|---|---|---|---|
| Y | Data | Agg | A,B | E | SUM | A |
| Y | Data | Agg2 | B | E | COUNT | A |
1行目は「Data シートを、A列とB列でグループ化し、E列を合計して Agg シートに出す」という意味です。
2行目は「Data シートを、B列でグループ化し、E列の件数を数えて Agg2 シートに出す」という意味です。
ここまで決めてしまえば、「どんな集計をしたいか」はすべて表で表現できます。
VBAは、この表を読み取って動くだけです。
コア部品:行明細を「キー→集計値」にまとめる汎用関数
グループキーを文字列として組み立てる
複数列でグループ化するために、「行の中からグループキーを作る」関数を用意します。
' ModAggEngine.bas
Option Explicit
Private Function BuildGroupKey(ByVal rowData As Variant, ByVal groupCols As Variant) As String
Dim i As Long
Dim parts() As String
ReDim parts(1 To UBound(groupCols) + 1)
For i = LBound(groupCols) To UBound(groupCols)
parts(i + 1) = CStr(rowData(1, CLng(groupCols(i))))
Next
BuildGroupKey = Join(parts, "||")
End Function
VBgroupCols は「{1,2}」のような列番号配列を想定しています。
キーは「値1||値2」のような文字列になります(区切り文字は被らないものにしておく)。
集計用の構造体と Dictionary
集計値を持つための構造体を定義します。
Private Type AggValue
Sum As Double
Count As Long
Max As Double
Min As Double
First As Variant
End Type
VBそして、「キー→AggValue」を持つ Dictionary を使って集計します。
集計設定を読み込む:ConfigAgg → ルール配列
集計ルール1行分の型を定義する
Private Type AggRule
Enabled As Boolean
SourceSheet As String
TargetSheet As String
GroupCols As Variant
ValueCol As Long
AggFunc As String
OutStartCol As Long
End Type
VB列番号は数字で扱いたいので、列記号(A,B,C…)でも番号に変換できるようにします。
Private Function ColToNumber(ByVal colRef As Variant) As Long
If IsNumeric(colRef) Then
ColToNumber = CLng(colRef)
Else
ColToNumber = Range(CStr(colRef) & "1").Column
End If
End Function
VBグループ列の「A,B」のような文字列を列番号配列に変換する関数も用意します。
Private Function ParseGroupCols(ByVal s As String) As Variant
Dim parts As Variant
parts = Split(s, ",")
Dim cols() As Long
ReDim cols(0 To UBound(parts))
Dim i As Long
For i = 0 To UBound(parts)
cols(i) = ColToNumber(Trim$(parts(i)))
Next
ParseGroupCols = cols
End Function
VBConfigAgg から AggRule 配列を作る
Private Function LoadAggRules() As Variant
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("ConfigAgg")
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
If lastRow < 2 Then
LoadAggRules = Empty
Exit Function
End If
Dim data As Variant
data = ws.Range(ws.Cells(2, 1), ws.Cells(lastRow, 7)).Value
Dim rules() As AggRule
ReDim rules(1 To UBound(data, 1))
Dim i As Long
For i = 1 To UBound(data, 1)
rules(i).Enabled = (UCase$(CStr(data(i, 1))) = "Y")
rules(i).SourceSheet = CStr(data(i, 2))
rules(i).TargetSheet = CStr(data(i, 3))
rules(i).GroupCols = ParseGroupCols(CStr(data(i, 4)))
rules(i).ValueCol = ColToNumber(data(i, 5))
rules(i).AggFunc = UCase$(CStr(data(i, 6)))
rules(i).OutStartCol = ColToNumber(data(i, 7))
Next
LoadAggRules = rules
End Function
VBこれで、「ConfigAgg の内容」が AggRule の配列としてメモリに載ります。
汎用集計エンジン:設定に従って集計を実行する
速度アップ用のラッパー
Private Sub SpeedOn()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
End Sub
Private Sub SpeedOff()
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
VBメイン処理:ルールごとに集計をかける
Public Sub RunAggTool()
Dim rules As Variant
rules = LoadAggRules()
If IsEmpty(rules) Then
MsgBox "ConfigAggに集計設定がありません。", vbInformation
Exit Sub
End If
SpeedOn
Dim i As Long
For i = LBound(rules) To UBound(rules)
If rules(i).Enabled Then
ApplyAggRule rules(i)
End If
Next i
SpeedOff
MsgBox "ノーコード集計ツールの処理が完了しました。", vbInformation
End Sub
VB1つの集計ルールを適用する汎用処理
ここが心臓部です。
Private Sub ApplyAggRule(ByRef rule As AggRule)
Dim wsS As Worksheet, wsT As Worksheet
Set wsS = ThisWorkbook.Worksheets(rule.SourceSheet)
On Error Resume Next
Set wsT = ThisWorkbook.Worksheets(rule.TargetSheet)
On Error GoTo 0
If wsT Is Nothing Then
Set wsT = ThisWorkbook.Worksheets.Add
wsT.Name = rule.TargetSheet
Else
wsT.Cells.Clear
End If
Dim lastRow As Long, lastCol As Long
lastRow = wsS.Cells(wsS.Rows.Count, 1).End(xlUp).Row
lastCol = wsS.Cells(1, wsS.Columns.Count).End(xlToLeft).Column
If lastRow < 2 Then Exit Sub
Dim data As Variant
data = wsS.Range(wsS.Cells(1, 1), wsS.Cells(lastRow, lastCol)).Value
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = 1
Dim r As Long
Dim key As String
Dim cols As Variant
cols = rule.GroupCols
For r = 2 To lastRow
key = BuildGroupKey(Application.Index(data, r, 0), cols)
If Not dict.Exists(key) Then
Dim av As AggValue
av.Sum = 0
av.Count = 0
av.Max = -1E+308
av.Min = 1E+308
av.First = data(r, rule.ValueCol)
dict.Add key, av
End If
Dim cur As AggValue
cur = dict(key)
Dim v As Variant
v = data(r, rule.ValueCol)
If IsNumeric(v) Then
cur.Sum = cur.Sum + CDbl(v)
If CDbl(v) > cur.Max Then cur.Max = CDbl(v)
If CDbl(v) < cur.Min Then cur.Min = CDbl(v)
End If
cur.Count = cur.Count + 1
dict(key) = cur
Next
Dim outRows As Long
outRows = dict.Count
Dim outData As Variant
ReDim outData(1 To outRows + 1, 1 To UBound(cols) + 2)
Dim i As Long, idx As Long
For i = 1 To UBound(cols)
outData(1, i) = wsS.Cells(1, cols(i - 1)).Value
Next
outData(1, UBound(cols) + 1) = rule.AggFunc
idx = 2
Dim k As Variant
For Each k In dict.Keys
Dim parts As Variant
parts = Split(CStr(k), "||")
For i = 0 To UBound(parts)
outData(idx, i + 1) = parts(i)
Next
Dim res As Variant
Dim av2 As AggValue
av2 = dict(k)
Select Case rule.AggFunc
Case "SUM": res = av2.Sum
Case "COUNT": res = av2.Count
Case "MAX": res = av2.Max
Case "MIN": res = av2.Min
Case "FIRST": res = av2.First
Case Else: res = av2.Sum
End Select
outData(idx, UBound(cols) + 1) = res
idx = idx + 1
Next
wsT.Range(wsT.Cells(1, rule.OutStartCol), wsT.Cells(outRows + 1, rule.OutStartCol + UBound(cols))).Value = outData
wsT.Columns.AutoFit
End Sub
VBここでの重要ポイントを整理します。
集計元シートを丸ごと配列に読み込んでいるので高速。
グループキーは BuildGroupKey で文字列化し、Dictionary のキーとして使う。
AggValue に Sum, Count, Max, Min, First を全部持たせておき、AggFunc に応じてどれを出すか切り替える。
出力シートはルールごとにクリアしてから書き出すので、毎回「最新の集計結果」だけが残る。
例題:売上明細を「店舗×月」で合計するノーコード集計
想定する元データ(Data)
Data シートに、次のような列があるとします。
A列:日付
B列:店舗コード
C列:商品コード
D列:数量
E列:売上金額
やりたいことは、「店舗×月ごとの売上金額合計」を出すことです。
月は、日付から「年月」を作ってグループ化しても良いですが、まずはシンプルに「店舗×日付」でグループ化する例で考えます。
ConfigAgg シートに、次のように書きます。
A列:Y
B列:Data
C列:Agg
D列:A,B
E列:E
F列:SUM
G列:A
これで、「Data を A列(日付)と B列(店舗コード)でグループ化し、E列(売上金額)を合計して Agg に出す」という設定になります。
RunAggTool を実行すると、Agg シートには
日付
店舗コード
SUM
という三列が出て、行ごとに「日付×店舗」の売上合計が並びます。
もし「店舗×月」で集計したければ、Data シートに「年月」列を追加しておき、
ConfigAgg の GroupCols を「F,B」のように変えるだけで対応できます(F列が年月だと仮定)。
重要ポイントの深掘り:ノーコード集計ツールを“現場で回る”形にするコツ
集計関数の種類を増やすときの考え方
今のテンプレは、SUM, COUNT, MAX, MIN, FIRST に対応しています。
もし「平均」を追加したければ、AggValue に Sum と Count が既にあるので、AggFunc=”AVG” のときに Sum / Count を出すだけで済みます。
Select Case rule.AggFunc の中に AVG を追加し、
res = IIf(av2.Count = 0, 0, av2.Sum / av2.Count)
のように書けばOKです。
このように、「AggValue に必要な情報を先に全部持たせておく」と、
あとから集計関数を増やすのがとても楽になります。
ConfigAgg の「検証」を入れておくべき理由
完全ノーコードにすると、現場の人が自由に設定を変えられる反面、
シート名のタイプミス、列指定のミス、集計関数名の誤記なども起こりやすくなります。
実務で使うなら、RunAggTool の最初に
指定された SourceSheet/TargetSheet が存在するか
GroupCols に指定された列が存在するか
AggFunc が許可された値(SUM, COUNT, MAX, MIN, AVG など)か
をチェックし、問題があれば処理を止めてメッセージを出す、という“安全装置”を付けておくと安心です。
ピボットテーブルとの住み分け
「集計ならピボットでいいのでは?」という疑問も出てきます。
ピボットは対話的な分析には最高ですが、
毎回同じ集計を同じ形で出したい
複数パターンの集計を一括で出したい
集計結果をさらに別のマクロやJOINの入力に使いたい
というときには、今回のような「配列+Dictionaryベースの集計エンジン」の方が向いています。
ConfigAgg に集計パターンを並べておけば、
「店舗×日」「店舗×月」「商品×月」「店舗×商品」など、
複数の集計を一気に回して、それぞれ別シートに出すことができます。
まとめ:ノーコード集計ツールは「集計を表にして、コードはエンジンに閉じ込める」発想
このテンプレの本質は、次の三つです。
どのシートを、どの列でグループ化し、どの列をどの関数で集計するかを、ConfigAgg という表にすべて書き出す。
VBA側は、その表を読み取り、配列と Dictionary を使って高速に集計する汎用エンジンだけを持つ。
集計内容を変えたいときは、ConfigAgg の行を追加・修正するだけで、コードは一切触らない。
これができると、「売上集計」「アクセスログ集計」「アンケート集計」など、
集計パターンがいくつ増えても、ツールは1本で済みます。
