単一キーJOIN
「コード(キー)で明細にマスタ情報を付けたい」「2つの集約表をキーで横に突き合わせたい」——単一キーJOINの定番テンプレを、初心者向けにかみ砕いてまとめました。最短はVLOOKUP、堅牢・高速は配列+辞書、列順が変わるなら見出し名で安全に参照します。
使い分けの指針
- 最短・少量: VLOOKUPを式投入→値化(簡単で速い導入)
- 列増減に強い: INDEX/MATCH(列番号ズレ問題を回避)
- 大量・高速: 配列+辞書でJOIN(10万行規模でも実用速度)
- 横結合(左結合・フル結合): 辞書でキー→レコードを作り、基準表へ付与
- 見出し名で安全: 列順変更や一部欠損に強い
最短:VLOOKUPで単一キーJOIN(式→値化)
Sub Join_ByVLookup()
'明細: A=コード, B=日付, C=金額
'マスタ: A=コード, B=名称, C=カテゴリ
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列
wsD.Range("D1:E1").Value = Array("名称", "カテゴリ")
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
End Sub
VB- ポイント
- 値化して式を撤去すれば、再計算負荷や参照ズレを回避。
- 列順が変わる可能性があるなら、次のINDEX/MATCHへ。
列増減に強い:INDEX/MATCHでJOIN(列番号不要)
Sub Join_ByIndexMatch()
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")
wsD.Range("D1:E1").Value = Array("名称", "カテゴリ")
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
End Sub
VB- ポイント
- 列の入れ替えや追加があっても安全。
- 値化で軽く・安定運用。
爆速・堅牢:配列+辞書でJOIN(単一キー)
Sub Join_ByDictionary()
'明細: A=コード, B=日付, C=金額
'マスタ: A=コード, B=名称, C=カテゴリ
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 dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim i As Long, key As String
For i = 2 To UBound(vm, 1)
key = UCase$(Trim$(CStr(vm(i, 1))))
If Len(key) > 0 Then dict(key) = Array(CStr(vm(i, 2)), CStr(vm(i, 3))) '名称,カテゴリ
Next
'2) 明細へ付与(配列で一括書戻し)
Dim vd As Variant: vd = wsD.Range("A1").CurrentRegion.Value
Dim out() As Variant: ReDim out(1 To UBound(vd, 1), 1 To UBound(vd, 2) + 2)
'ヘッダー
Dim 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) = "カテゴリ"
'行ループ
Dim r As Long
For r = 2 To UBound(vd, 1)
For c = 1 To UBound(vd, 2): out(r, c) = vd(r, c): Next
key = UCase$(Trim$(CStr(vd(r, 1))))
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- ポイント
- キー正規化(Trim/UCase)で表記揺れを吸収。
- セル往復ゼロで高速。未登録は「#N/A」で見える化。
左結合・フル結合の横統合(単一キー)
左結合(基準表に他表指標を横付け)
Sub Merge_LeftJoin()
'基準: Sheet("基準") A=コード, B=名称, C=合計
'追加: Sheet("他") A=コード, B=他合計
'出力: Sheet("統合")
Dim wsB As Worksheet: Set wsB = Worksheets("基準")
Dim wsO As Worksheet: Set wsO = Worksheets("他")
Dim wsOut As Worksheet
On Error Resume Next
Set wsOut = Worksheets("統合")
If wsOut Is Nothing Then Set wsOut = Worksheets.Add: wsOut.Name = "統合"
On Error GoTo 0
wsOut.Cells.Clear
wsOut.Range("A1:D1").Value = Array("コード", "名称", "合計", "他合計")
'追加表辞書(コード→他合計)
Dim vo As Variant: vo = wsO.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(vo, 1)
key = UCase$(Trim$(CStr(vo(i, 1))))
dict(key) = Val(vo(i, 2))
Next
'基準を出力しながら横付け
Dim vb As Variant: vb = wsB.Range("A1").CurrentRegion.Value
Dim rOut As Long: rOut = 2
For i = 2 To UBound(vb, 1)
key = UCase$(Trim$(CStr(vb(i, 1))))
wsOut.Cells(rOut, 1).Value = vb(i, 1)
wsOut.Cells(rOut, 2).Value = vb(i, 2)
wsOut.Cells(rOut, 3).Value = Val(vb(i, 3))
wsOut.Cells(rOut, 4).Value = IIf(dict.Exists(key), dict(key), 0)
rOut = rOut + 1
Next
wsOut.Columns.AutoFit
End Sub
VBフル結合(両方にあるキーは統合、片側のみは0/空欄で補完)
Sub Merge_FullJoin()
Dim wsB As Worksheet: Set wsB = Worksheets("基準")
Dim wsO As Worksheet: Set wsO = Worksheets("他")
Dim wsOut As Worksheet
On Error Resume Next
Set wsOut = Worksheets("統合")
If wsOut Is Nothing Then Set wsOut = Worksheets.Add: wsOut.Name = "統合"
On Error GoTo 0
wsOut.Cells.Clear
wsOut.Range("A1:D1").Value = Array("コード", "名称", "合計", "他合計")
Dim vb As Variant: vb = wsB.Range("A1").CurrentRegion.Value
Dim vo As Variant: vo = wsO.Range("A1").CurrentRegion.Value
'基準辞書(コード→(名称,合計))
Dim base As Object: Set base = CreateObject("Scripting.Dictionary")
Dim i As Long, key As String
For i = 2 To UBound(vb, 1)
key = UCase$(Trim$(CStr(vb(i, 1))))
base(key) = Array(CStr(vb(i, 2)), Val(vb(i, 3)))
Next
'他辞書(コード→他合計)
Dim other As Object: Set other = CreateObject("Scripting.Dictionary")
For i = 2 To UBound(vo, 1)
key = UCase$(Trim$(CStr(vo(i, 1))))
other(key) = Val(vo(i, 2))
Next
'キーの和集合で出力
Dim all As Object: Set all = CreateObject("Scripting.Dictionary")
For Each key In base.Keys: all(key) = True: Next
For Each key In other.Keys: all(key) = True: Next
Dim rOut As Long: rOut = 2
Dim k As Variant
For Each k In all.Keys
wsOut.Cells(rOut, 1).Value = k
wsOut.Cells(rOut, 2).Value = IIf(base.Exists(k), base(k)(0), "")
wsOut.Cells(rOut, 3).Value = IIf(base.Exists(k), base(k)(1), 0)
wsOut.Cells(rOut, 4).Value = IIf(other.Exists(k), other(k), 0)
rOut = rOut + 1
Next
wsOut.Columns.AutoFit
End Sub
VB- ポイント
- 左結合は「基準にないキーは捨てる」、フル結合は「両側の全キーを出す」。
- 補完値は0/空欄など、運用に合わせて選択。
見出し名で安全に列を特定(列順変更に強い)
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
VBSub Join_ByHeaderNames()
'見出し名で列を取得してJOIN(列順が変わっても壊れない)
Dim wsD As Worksheet: Set wsD = Worksheets("明細")
Dim wsM As Worksheet: Set wsM = Worksheets("マスタ")
Dim rgD As Range: Set rgD = wsD.Range("A1").CurrentRegion
Dim rgM As Range: Set rgM = wsM.Range("A1").CurrentRegion
Dim cKeyD As Long: cKeyD = FindHeader(rgD.Rows(1), "コード")
Dim cKeyM As Long: cKeyM = FindHeader(rgM.Rows(1), "コード")
Dim cNameM As Long: cNameM = FindHeader(rgM.Rows(1), "名称")
Dim cCatM As Long: cCatM = FindHeader(rgM.Rows(1), "カテゴリ")
If cKeyD * cKeyM * cNameM * cCatM = 0 Then MsgBox "見出しが不足しています": Exit Sub
Dim vd As Variant: vd = rgD.Value
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 = UCase$(Trim$(CStr(vm(i, cKeyM))))
dict(key) = Array(CStr(vm(i, cNameM)), CStr(vm(i, cCatM)))
Next
Dim out() As Variant: ReDim out(1 To UBound(vd, 1), 1 To UBound(vd, 2) + 2)
Dim 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) = "カテゴリ"
Dim r As Long
For r = 2 To UBound(vd, 1)
For c = 1 To UBound(vd, 2): out(r, c) = vd(r, c): Next
key = UCase$(Trim$(CStr(vd(r, cKeyD))))
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- ポイント
- 列名が変わっても、Findで動的に列番号取得。
- 欠損列はフラグ付けで監査しやすく。
よくある落とし穴と対策
- キーの表記揺れでJOIN失敗
- 対策:
Trim/UCase、必要なら半角化や不要記号の置換で正規化。
- 対策:
- 数値が文字列で計算ずれ
- 対策: 付与時に
Valで数値化、または元データを値化して型を揃える。
- 対策: 付与時に
- 列順変更・列追加でコードが壊れる
- 対策: 見出し名で列特定(FindHeader)、INDEX/MATCH採用。
- 大規模で遅い
- 対策: 範囲→配列→辞書→一括書戻し。前後で
ScreenUpdating/Calculation/Eventsを停止→復帰。
- 対策: 範囲→配列→辞書→一括書戻し。前後で
例題で練習
'例1:VLOOKUPで単一キーJOIN(式→値化)
Sub Example_VLookup()
Join_ByVLookup
End Sub
'例2:INDEX/MATCHで列増減に強いJOIN
Sub Example_IndexMatch()
Join_ByIndexMatch
End Sub
'例3:配列+辞書で高速JOIN
Sub Example_DictJoin()
Join_ByDictionary
End Sub
'例4:基準表に他指標を左結合
Sub Example_LeftJoin()
Merge_LeftJoin
End Sub
'例5:フル結合で両テーブルを完全統合
Sub Example_FullJoin()
Merge_FullJoin
End Sub
'例6:見出し名で列特定して安全JOIN
Sub Example_ByHeaders()
Join_ByHeaderNames
End Sub
VB