左外部結合
「基準表のキーを基準に、他表の情報を“ある分だけ”横付けしたい(なければ空欄や0)」——それが左外部結合です。初心者でも壊れないように、最短の関数型、堅牢・高速な辞書型、見出し名対応の安全型を用意しました。
使い分けの指針
- 最短・少量: VLOOKUPを入れて値化(導入が速い)
- 列増減に強い: INDEX/MATCH(列番号ズレ問題を回避)
- 大量・高速: 配列+辞書(10万行でも現実的)
- 列順が不安: 見出し名から列特定して安全に参照
最短:VLOOKUPで左外部結合(式→値化)
Sub LeftJoin_VLookup()
'基準: Sheet("基準") A=コード, B=名称, C=合計
'追加: Sheet("他") A=コード, B=他合計
'出力は基準シートのD列に「他合計」を付与
Dim wsB As Worksheet: Set wsB = Worksheets("基準")
Dim wsO As Worksheet: Set wsO = Worksheets("他")
Dim lastB As Long: lastB = wsB.Cells(wsB.Rows.Count, "A").End(xlUp).Row
Dim tblO As Range: Set tblO = wsO.Range("A1").CurrentRegion 'A:B
wsB.Range("D1").Value = "他合計"
With wsB.Range("D2:D" & lastB)
.FormulaR1C1 = "=IFERROR(VLOOKUP(RC1," & tblO.Address(True, True, xlA1, True) & ",2,FALSE),0)"
.Value = .Value '値化して式撤去
End With
End Sub
VB- ポイント
- 見出しを含む範囲を参照: CurrentRegionでA:Bを丸ごと指定。
- IFERRORで欠損補完: 追加表にないキーは0や空欄に。
堅牢:INDEX/MATCHで左外部結合(列番号不要)
Sub LeftJoin_IndexMatch()
Dim wsB As Worksheet: Set wsB = Worksheets("基準")
Dim wsO As Worksheet: Set wsO = Worksheets("他")
Dim lastB As Long: lastB = wsB.Cells(wsB.Rows.Count, "A").End(xlUp).Row
Dim rngKeyO As Range: Set rngKeyO = wsO.Range("A:A") '追加キー列
Dim rngValO As Range: Set rngValO = wsO.Range("B:B") '追加値列
wsB.Range("D1").Value = "他合計"
With wsB.Range("D2:D" & lastB)
.FormulaR1C1 = "=IFERROR(INDEX(" & rngValO.Address(True, True, xlA1, True) & _
",MATCH(RC1," & rngKeyO.Address(True, True, xlA1, True) & ",0)),0)"
.Value = .Value
End With
End Sub
VB- ポイント
- 列増減に強い: 参照は列範囲に紐付けるので、列を足しても壊れにくい。
- 値化で軽量運用: 式は貼った直後に値化。
爆速・堅牢:配列+辞書で左外部結合(大量行向け)
Sub LeftJoin_Dictionary()
'基準: A=コード, B=名称, C=合計
'追加: 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))))
If Len(key) > 0 Then dict(key) = Val(vo(i, 2))
Next
'基準表→配列→出力
Dim vb As Variant: vb = wsB.Range("A1").CurrentRegion.Value
Dim out() As Variant: ReDim out(1 To UBound(vb, 1), 1 To 4)
'ヘッダー
out(1, 1) = "コード": out(1, 2) = "名称": out(1, 3) = "合計": out(1, 4) = "他合計"
Dim r As Long
For r = 2 To UBound(vb, 1)
Dim k As String: k = UCase$(Trim$(CStr(vb(r, 1))))
out(r, 1) = vb(r, 1)
out(r, 2) = vb(r, 2)
out(r, 3) = Val(vb(r, 3))
out(r, 4) = IIf(dict.Exists(k), dict(k), 0)
Next
wsOut.Range("A1").Resize(UBound(out, 1), 4).Value = out
wsOut.Columns.AutoFit
End Sub
VB- ポイント
- キー正規化: Trim/UCaseで表記揺れを吸収。
- セル往復ゼロ: 範囲→配列→辞書→一括貼付で高速。
見出し名で安全に列特定(列順変更に強い)
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
Sub LeftJoin_ByHeaders()
'基準・追加とも見出しから列番号を特定して左結合
Dim wsB As Worksheet: Set wsB = Worksheets("基準")
Dim wsO As Worksheet: Set wsO = Worksheets("他")
Dim rgB As Range: Set rgB = wsB.Range("A1").CurrentRegion
Dim rgO As Range: Set rgO = wsO.Range("A1").CurrentRegion
Dim cKeyB As Long: cKeyB = FindHeader(rgB.Rows(1), "コード")
Dim cNameB As Long: cNameB = FindHeader(rgB.Rows(1), "名称")
Dim cSumB As Long: cSumB = FindHeader(rgB.Rows(1), "合計")
Dim cKeyO As Long: cKeyO = FindHeader(rgO.Rows(1), "コード")
Dim cValO As Long: cValO = FindHeader(rgO.Rows(1), "他合計")
If cKeyB * cNameB * cSumB * cKeyO * cValO = 0 Then MsgBox "見出し不足": Exit Sub
'追加→辞書
Dim vo As Variant: vo = rgO.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, cKeyO))))
dict(key) = Val(vo(i, cValO))
Next
'基準→出力
Dim vb As Variant: vb = rgB.Value
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 rOut As Long: rOut = 2
For i = 2 To UBound(vb, 1)
key = UCase$(Trim$(CStr(vb(i, cKeyB))))
wsOut.Cells(rOut, 1).Value = vb(i, cKeyB)
wsOut.Cells(rOut, 2).Value = vb(i, cNameB)
wsOut.Cells(rOut, 3).Value = Val(vb(i, cSumB))
wsOut.Cells(rOut, 4).Value = IIf(dict.Exists(key), dict(key), 0)
rOut = rOut + 1
Next
wsOut.Columns.AutoFit
End Sub
VB- ポイント
- 列順変動に耐性: Findで列を動的に取得。
- 欠損補完: ないキーは0や空欄で埋める。
よくある落とし穴と対策
- キー表記揺れで結合失敗
- 対策: 正規化: Trim/UCase、必要なら半角化や記号除去。
- 数値が文字列で合計ずれ
- 対策: 数値化: Valで安全に数値へ。式は
.Value = .Valueで値化。
- 対策: 数値化: Valで安全に数値へ。式は
- 列順変更で壊れる
- 対策: 見出し名特定: FindHeaderで列取得、INDEX/MATCHを採用。
- 大規模で遅い
- 対策: 配列+辞書: 範囲→配列→辞書→一括貼付。前後で ScreenUpdating/Calculation/Events を停止→復帰。
例題で練習
'例1:VLOOKUPで左外部結合(式→値化)
Sub Example_Left_VLookup()
LeftJoin_VLookup
End Sub
'例2:INDEX/MATCHで左外部結合(列増減に強い)
Sub Example_Left_IndexMatch()
LeftJoin_IndexMatch
End Sub
'例3:配列+辞書で左外部結合(高速)
Sub Example_Left_Dict()
LeftJoin_Dictionary
End Sub
'例4:見出し名で列特定して安全に左外部結合
Sub Example_Left_ByHeaders()
LeftJoin_ByHeaders
End Sub
VB