マスタとの集計統合
「明細データにマスタの属性を付けてから集計したい」「コードから正式名称へ置き換えて集計したい」——VLOOKUP相当、INDEX/MATCH相当、辞書での高速ジョイン、未登録キーの扱いまで、初心者でも壊れないテンプレをまとめました。
目的と選び方
- 明細にマスタ項目を付ける(1対多の参照): 辞書でキー→レコードを引く(高速・頑丈)
- 関数で手軽に付与: WorksheetFunction.VLookup / Index + Match(少量・簡単)
- マスタ属性で集計したい: マスタでカテゴリを付与→辞書/pivotでグループ集計
- 未登録キー対策と監査: 未一致をフラグ、一覧を作って追加依頼へ
基本テンプレ:辞書で「コード→名称・カテゴリ」を付与(高速ジョイン)
'明細シート:A=コード, B=日付, C=金額
'マスタシート:A=コード, B=正式名称, C=カテゴリ
Sub JoinMaster_WithDictionary()
Dim wsD As Worksheet: Set wsD = Worksheets("明細")
Dim wsM As Worksheet: Set wsM = Worksheets("マスタ")
'マスタを配列→辞書化(キー:コード)
Dim rgM As Range: Set rgM = wsM.Range("A1").CurrentRegion
Dim vm As Variant: vm = rgM.Value
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim i As Long, key As String
For i = 2 To UBound(vm, 1)
key = Trim$(CStr(vm(i, 1))) 'コード
If Len(key) > 0 Then
'名称・カテゴリを格納(配列で保持)
dict(key) = Array(CStr(vm(i, 2)), CStr(vm(i, 3)))
End If
Next
'明細側を配列に読み込み→名称・カテゴリを列追加して書き戻し
Dim rgD As Range: Set rgD = wsD.Range("A1").CurrentRegion
Dim vd As Variant: vd = rgD.Value
'出力列ヘッダー(末尾2列追加:名称・カテゴリ)
Dim out() As Variant: ReDim out(1 To UBound(vd, 1), 1 To UBound(vd, 2) + 2)
Dim r As Long, c As Long
'ヘッダーコピー+追加
For c = 1 To UBound(vd, 2): out(1, c) = vd(1, c): Next
out(1, UBound(vd, 2) + 1) = "名称"
out(1, UBound(vd, 2) + 2) = "カテゴリ"
'明細行に付与
For r = 2 To UBound(vd, 1)
For c = 1 To UBound(vd, 2)
out(r, c) = vd(r, c)
Next
key = Trim$(CStr(vd(r, 1))) 'A列コード
If dict.Exists(key) Then
out(r, UBound(vd, 2) + 1) = dict(key)(0)
out(r, UBound(vd, 2) + 2) = dict(key)(1)
Else
out(r, UBound(vd, 2) + 1) = "#N/A"
out(r, UBound(vd, 2) + 2) = "#N/A"
End If
Next
'書き戻し(元表をクリアして上書き)
wsD.Range("A1").Resize(UBound(out, 1), UBound(out, 2)).Value = out
End Sub
VB- ポイント:
- 配列+辞書で高速: セル往復ゼロ。10万行規模でも現実的。
- 未一致は「#N/A」: 後で監査しやすい。必要なら別列に「未登録」フラグを追加。
関数版:VLOOKUP相当をそのまま使う(少量・簡単)
Sub JoinMaster_WithVLookup()
Dim wsD As Worksheet: Set wsD = Worksheets("明細")
Dim wsM As Worksheet: Set wsM = Worksheets("マスタ")
Dim lastD As Long: lastD = wsD.Cells(wsD.Rows.Count, "A").End(xlUp).Row
Dim tblM As Range: Set tblM = wsM.Range("A1").CurrentRegion 'A:C
'名称をD列、カテゴリをE列に付与
With wsD.Range("D2:D" & lastD)
.FormulaR1C1 = "=IFERROR(VLOOKUP(RC1," & tblM.Address(True, True, xlA1, True) & ",2,FALSE),""#N/A"")"
.Value = .Value '値化
End With
With wsD.Range("E2:E" & lastD)
.FormulaR1C1 = "=IFERROR(VLOOKUP(RC1," & tblM.Address(True, True, xlA1, True) & ",3,FALSE),""#N/A"")"
.Value = .Value
End With
wsD.Range("D1").Value = "名称": wsD.Range("E1").Value = "カテゴリ"
End Sub
VB- ポイント:
- 値化して式撤去: 再計算負荷を避けて安定運用。
- A1参照の絶対アドレス: Address(…, True)で参照がズレない。
INDEX/MATCH相当:列の増減に強い
Sub JoinMaster_WithIndexMatch()
Dim wsD As Worksheet: Set wsD = Worksheets("明細")
Dim wsM As Worksheet: Set wsM = Worksheets("マスタ")
Dim lastD As Long: lastD = wsD.Cells(wsD.Rows.Count, "A").End(xlUp).Row
Dim rngKey As Range: Set rngKey = wsM.Range("A:A")
Dim rngName As Range: Set rngName = wsM.Range("B:B")
Dim rngCat As Range: Set rngCat = wsM.Range("C:C")
With wsD.Range("D2:D" & lastD)
.FormulaR1C1 = "=IFERROR(INDEX(" & rngName.Address(True, True, xlA1, True) & ",MATCH(RC1," & rngKey.Address(True, True, xlA1, True) & ",0)),""#N/A"")"
.Value = .Value
End With
With wsD.Range("E2:E" & lastD)
.FormulaR1C1 = "=IFERROR(INDEX(" & rngCat.Address(True, True, xlA1, True) & ",MATCH(RC1," & rngKey.Address(True, True, xlA1, True) & ",0)),""#N/A"")"
.Value = .Value
End With
wsD.Range("D1").Value = "名称": wsD.Range("E1").Value = "カテゴリ"
End Sub
VB- ポイント:
- 列順が変わっても安全: 参照範囲を列単位に。VLOOKUPの列番号ズレ問題を回避。
統合後の「カテゴリ別集計」(マスタ属性でグループ化)
'明細A=コード, C=金額、マスタでカテゴリ付けしてから集計
Sub Aggregate_ByMasterCategory()
Dim wsD As Worksheet: Set wsD = Worksheets("明細")
Dim wsM As Worksheet: Set wsM = Worksheets("マスタ")
'1) マスタ辞書:コード→カテゴリ
Dim vm As Variant: vm = wsM.Range("A1").CurrentRegion.Value
Dim dictCat As Object: Set dictCat = CreateObject("Scripting.Dictionary")
Dim i As Long, key As String
For i = 2 To UBound(vm, 1)
key = Trim$(CStr(vm(i, 1)))
If Len(key) > 0 Then dictCat(key) = CStr(vm(i, 3))
Next
'2) 明細を読み→カテゴリで合計/件数
Dim vd As Variant: vd = wsD.Range("A1").CurrentRegion.Value
Dim sumMap As Object: Set sumMap = CreateObject("Scripting.Dictionary")
Dim cntMap As Object: Set cntMap = CreateObject("Scripting.Dictionary")
For i = 2 To UBound(vd, 1)
key = Trim$(CStr(vd(i, 1))) 'コード
Dim cat As String
If dictCat.Exists(key) Then cat = dictCat(key) Else cat = "未登録"
If Not sumMap.Exists(cat) Then sumMap.Add cat, 0#: cntMap.Add cat, 0
sumMap(cat) = sumMap(cat) + Val(vd(i, 3))
cntMap(cat) = cntMap(cat) + 1
Next
'3) 出力(シート「集計」へ)
Dim wsO As Worksheet: Set wsO = Worksheets("集計")
wsO.Range("A1:C1").Value = Array("カテゴリ", "合計金額", "件数")
Dim k As Variant, rOut As Long: rOut = 2
For Each k In sumMap.Keys
wsO.Cells(rOut, 1).Value = k
wsO.Cells(rOut, 2).Value = sumMap(k)
wsO.Cells(rOut, 3).Value = cntMap(k)
rOut = rOut + 1
Next
End Sub
VB- ポイント:
- 「未登録」バケット: マスタ未一致を落とさず可視化。
- 追加軸も簡単: キーを「カテゴリ|年月」などへ拡張すれば多軸集計が一撃。
未登録キーの監査リストを作る(運用必須)
Sub List_UnmatchedKeys()
Dim wsD As Worksheet: Set wsD = Worksheets("明細")
Dim wsM As Worksheet: Set wsM = Worksheets("マスタ")
Dim vd As Variant: vd = wsD.Range("A1").CurrentRegion.Value
Dim vm As Variant: vm = wsM.Range("A1").CurrentRegion.Value
'マスタのキー集合
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim i As Long, key As String
For i = 2 To UBound(vm, 1)
key = Trim$(CStr(vm(i, 1)))
If Len(key) > 0 Then dict(key) = True
Next
'未一致キーを一意に収集
Dim miss As Object: Set miss = CreateObject("Scripting.Dictionary")
For i = 2 To UBound(vd, 1)
key = Trim$(CStr(vd(i, 1)))
If Len(key) > 0 And Not dict.Exists(key) Then miss(key) = True
Next
'出力
Dim wsO As Worksheet: Set wsO = Worksheets("未登録")
wsO.Cells.Clear
wsO.Range("A1").Value = "未登録コード"
Dim r As Long: r = 2
Dim k As Variant
For Each k In miss.Keys
wsO.Cells(r, 1).Value = k
r = r + 1
Next
End Sub
VB- ポイント:
- 監査の見える化: マスタ整備の抜け漏れを定期的に洗い出す。
マスタに存在しない場合のフォールバック(暫定名称)
'未一致なら「コードそのまま」や「暫定名」をつける例
Function ResolveName(ByVal code As String, ByVal dict As Object) As String
If dict.Exists(code) Then
ResolveName = dict(code)(0) '名称
Else
ResolveName = "[暫定]" & code
End If
End Function
VB- ポイント:
- 運用継続重視: 解析や可視化を止めないための暫定措置。
ピボット前処理:マスタ結合→ピボット自動作成
Sub JoinThenPivot()
'1) マスタ結合(辞書版を呼ぶ)で名称・カテゴリ列を付与
Call JoinMaster_WithDictionary
'2) ピボットを作成(カテゴリ×月×金額合計)
Dim src As Range: Set src = Worksheets("明細").Range("A1").CurrentRegion
Dim outWs As Worksheet
On Error Resume Next
Set outWs = Worksheets("ピボット")
If outWs Is Nothing Then Set outWs = Worksheets.Add: outWs.Name = "ピボット"
On Error GoTo 0
Dim pc As PivotCache, pt As PivotTable
Set pc = ActiveWorkbook.PivotCaches.Create(xlDatabase, src)
Set pt = pc.CreatePivotTable(outWs.Range("A3"), "統合ピボット")
With pt
.PivotFields("カテゴリ").Orientation = xlRowField
.PivotFields("日付").Orientation = xlColumnField
.PivotFields("日付").NumberFormat = "yyyy-mm"
With .PivotFields("金額")
.Orientation = xlDataField
.Function = xlSum
.NumberFormat = "#,##0"
End With
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- ポイント:
- 処理前後で挟むだけで高速化。 途中エラー時も復帰が確実。
よくある落とし穴と対策
- ラベル揺れ(全角/半角・前後空白)で未一致が多発
- 対策: 参照時に Trim、必要なら StrConv(vbNarrow) で半角統一、UCase/LCaseで大小文字をそろえる。
- VLOOKUPの列番号ズレで誤参照
- 対策: INDEX/MATCHに切り替える、または辞書で明示的に列をマッピング。
- 未登録を見落として集計が偏る
- 対策: 未一致を「未登録」バケットに集め、監査リストを定期出力。
- セル往復で遅い
- 対策: 範囲→配列→辞書→一括書き戻しにする。式を使う場合は投入後に値化。
- 列順変更で壊れる
- 対策: CurrentRegion+見出し名運用、INDEX/MATCHや辞書に切り替え。
例題で練習
'例1:辞書で名称・カテゴリを付与して明細に書き戻す
Sub Example_JoinDict()
Call JoinMaster_WithDictionary
End Sub
'例2:VLOOKUPで手早く付与して値化
Sub Example_JoinVLookup()
Call JoinMaster_WithVLookup
End Sub
'例3:マスタのカテゴリを用いてグループ集計(未登録含む)
Sub Example_AggByCategory()
Call Aggregate_ByMasterCategory
End Sub
'例4:結合後にピボットを自動作成
Sub Example_JoinThenPivot()
Call JoinThenPivot
End Sub
'例5:未登録コードの監査リストを作成
Sub Example_ListUnmatched()
Call List_UnmatchedKeys
End Sub
VB