重複チェック
重複は“気づかないうちに蓄積する”から厄介。だからこそ、初心者でも安心して使える「見える化」「抽出」「防止」のテンプレをまとめました。コツは「キーの正規化(Trim+大文字化)」「配列+Dictionaryで高速突合」「手戻りしない安全設計」です。
共通ユーティリティ(正規化・速度)
Option Explicit
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
Private Function NormKey(ByVal v As Variant) As String
NormKey = UCase$(Trim$(CStr(v)))
End Function
Private Function EnsureSheet(ByVal name As String, Optional ByVal clear As Boolean = True) As Worksheet
Dim ws As Worksheet
On Error Resume Next
Set ws = Worksheets(name)
On Error GoTo 0
If ws Is Nothing Then
Set ws = Worksheets.Add(After:=Worksheets(Worksheets.Count))
ws.Name = name
End If
If clear Then ws.Cells.Clear
Set EnsureSheet = ws
End Function
VB- 正規化: 表記揺れ(前後空白、大小文字)で重複判定がズレないように。
- 速度: 画面更新・イベント・計算を止める→復帰。
基本:単一列の重複を色付け
A列の重複セルを黄色(1回以上の重複)に塗ります。見出しはA1。
Sub HighlightDuplicates_InColumn()
SpeedOn
Dim ws As Worksheet: Set ws = ActiveSheet
Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim i As Long, k As String
'一旦クリア
ws.Range("A2:A" & lastRow).Interior.ColorIndex = xlNone
For i = 2 To lastRow
k = NormKey(ws.Cells(i, "A").Value)
If Len(k) = 0 Then GoTo cont
If dict.Exists(k) Then
'今回と過去の出現を塗る
ws.Cells(i, "A").Interior.Color = vbYellow
Else
dict(k) = i
End If
cont:
Next
'過去分もまとめて塗る(任意・高速を重視するなら省略可)
Dim key As Variant
For Each key In dict.Keys
If WorksheetFunction.CountIf(ws.Range("A2:A" & lastRow), key) > 1 Then
ws.Cells(dict(key), "A").Interior.Color = vbYellow
End If
Next
SpeedOff
MsgBox "重複セルをハイライトしました。"
End Sub
VB- ポイント: 初回/2回目以降両方塗ると「重複箇所」が一目で分かる。
抽出:重複一覧を別シートへ出力(単一列)
A列の重複値と行番号を「重複一覧」へ出力します。
Sub ListDuplicates_InColumn()
SpeedOn
Dim ws As Worksheet: Set ws = ActiveSheet
Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim hits As Object: Set hits = CreateObject("Scripting.Dictionary")
Dim i As Long, k As String
For i = 2 To lastRow
k = NormKey(ws.Cells(i, "A").Value)
If Len(k) = 0 Then GoTo cont
If dict.Exists(k) Then
'出現位置を蓄積
hits(k) = hits(k) & IIf(hits.Exists(k), "," & i, i)
Else
dict(k) = True
hits(k) = CStr(i)
End If
cont:
Next
'2回以上の出現だけ抽出
Dim outWs As Worksheet: Set outWs = EnsureSheet("重複一覧", True)
outWs.Range("A1:C1").Value = Array("値", "出現回数", "行番号一覧")
Dim r As Long: r = 2
Dim key As Variant
For Each key In hits.Keys
Dim arr() As String: arr = Split(hits(key), ",")
If UBound(arr) >= 1 Then
outWs.Cells(r, 1).Value = key
outWs.Cells(r, 2).Value = UBound(arr) + 1
outWs.Cells(r, 3).Value = hits(key)
r = r + 1
End If
Next
outWs.Columns.AutoFit
SpeedOff
MsgBox "重複一覧を作成しました。"
End Sub
VB- ポイント: 行番号一覧を残すと位置特定が楽。
複数列で重複(複合キー)
「コード×日付」などの組み合わせで重複判定します。
Private Function BuildCompositeKey(ByVal code As Variant, ByVal ymd As Variant) As String
Dim part2 As String
If IsDate(ymd) Then part2 = Format$(CDate(ymd), "yyyy-mm-dd") Else part2 = CStr(ymd)
BuildCompositeKey = NormKey(code) & "|" & UCase$(Trim$(part2))
End Function
Sub HighlightDuplicates_MultiColumns()
SpeedOn
Dim ws As Worksheet: Set ws = ActiveSheet
'列想定:A=コード、B=日付
Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
ws.Range("A2:B" & lastRow).Interior.ColorIndex = xlNone
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim i As Long, key As String
For i = 2 To lastRow
key = BuildCompositeKey(ws.Cells(i, "A").Value, ws.Cells(i, "B").Value)
If Len(key) = 0 Then GoTo cont
If dict.Exists(key) Then
ws.Range("A" & i & ":B" & i).Interior.Color = vbYellow
'元の行も塗っておきたい場合
ws.Range("A" & dict(key) & ":B" & dict(key)).Interior.Color = vbYellow
Else
dict(key) = i
End If
cont:
Next
SpeedOff
MsgBox "複合キーの重複をハイライトしました。"
End Sub
VB- ポイント: 区切りは安全な文字(|)で連結。日付は yyyy-mm-dd に統一。
防止:入力時に重複を拒否(Worksheet_Change)
入力直後に同一値がある場合、赤塗り・メッセージで通知して元に戻す例。A列を監視。
'シートモジュール(監視したいシートのコードウィンドウ)に貼付
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo fin
If Target.Columns.Count > 1 Then Exit Sub
If Target.Column <> 1 Then Exit Sub 'A列のみ
Application.EnableEvents = False
Dim cell As Range
For Each cell In Target.Cells
Dim k As String: k = NormKey(cell.Value)
If Len(k) = 0 Then GoTo cont
Dim rng As Range: Set rng = Range("A2:A" & Cells(Rows.Count, "A").End(xlUp).Row)
If WorksheetFunction.CountIf(rng, cell.Value) > 1 Then
cell.Interior.Color = vbRed
MsgBox "重複を検知しました: " & cell.Value
'任意:元値を消す(コメントアウトで通知のみ)
'cell.Value = ""
Else
cell.Interior.ColorIndex = xlNone
End If
cont:
Next
fin:
Application.EnableEvents = True
End Sub
VB- ポイント: イベント再帰防止のため
EnableEvents=False/Trueを忘れない。
実務:重複行の安全削除(下から)
重複値が2回以上ある場合、“2回目以降”を削除してユニーク化します。A列対象。
Sub RemoveDuplicates_Safe()
SpeedOn
Dim ws As Worksheet: Set ws = ActiveSheet
Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim i As Long, k As String
Dim delRows As Collection: Set delRows = New Collection
For i = 2 To lastRow
k = NormKey(ws.Cells(i, "A").Value)
If Len(k) = 0 Then GoTo cont
If dict.Exists(k) Then
delRows.Add i
Else
dict(k) = True
End If
cont:
Next
'下から削除
Dim r As Long
For r = delRows.Count To 1 Step -1
ws.Rows(delRows(r)).Delete
Next
SpeedOff
MsgBox "重複削除完了: " & delRows.Count & "行"
End Sub
VB- ポイント: 上から削除すると行が詰まってズレます。必ず“下から”。
応用:複数列の重複を一覧出力
「コード×日付×枝番」などの3列複合キーに対応。
Sub ListDuplicates_MultiColumns()
SpeedOn
Dim ws As Worksheet: Set ws = ActiveSheet
Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim pos As Object: Set pos = CreateObject("Scripting.Dictionary")
Dim i As Long, key As String
For i = 2 To lastRow
key = NormKey(ws.Cells(i, "A").Value) & "|" & _
NormKey(ws.Cells(i, "B").Value) & "|" & _
NormKey(ws.Cells(i, "C").Value)
If dict.Exists(key) Then
pos(key) = pos(key) & "," & i
Else
dict(key) = True
pos(key) = CStr(i)
End If
Next
Dim wsOut As Worksheet: Set wsOut = EnsureSheet("複合重複一覧", True)
wsOut.Range("A1:D1").Value = Array("複合キー", "出現回数", "行番号一覧", "例:コード|日付|枝番")
Dim r As Long: r = 2
Dim k As Variant
For Each k In pos.Keys
Dim arr() As String: arr = Split(pos(k), ",")
If UBound(arr) >= 1 Then
wsOut.Cells(r, 1).Value = k
wsOut.Cells(r, 2).Value = UBound(arr) + 1
wsOut.Cells(r, 3).Value = pos(k)
wsOut.Cells(r, 4).Value = ""
r = r + 1
End If
Next
wsOut.Columns.AutoFit
SpeedOff
MsgBox "複合重複一覧を作成しました。"
End Sub
VB例題で練習
'例1:A列の重複セルをハイライト
Sub Example_HighlightSingle()
HighlightDuplicates_InColumn
End Sub
'例2:A列の重複一覧を作成(値・回数・行番号)
Sub Example_ListSingle()
ListDuplicates_InColumn
End Sub
'例3:A×Bの複合キーで重複をハイライト
Sub Example_HighlightMulti()
HighlightDuplicates_MultiColumns
End Sub
'例4:A列の重複行を安全に削除(2回目以降)
Sub Example_RemoveSafe()
RemoveDuplicates_Safe
End Sub
'例5:A×B×Cの複合キー重複を一覧化
Sub Example_ListMulti()
ListDuplicates_MultiColumns
End Sub
VB実務の落とし穴と対策
- 表記揺れで判定ミス: NormKey(Trim+大文字化)を必ず使う。半角/全角混在なら置換も検討。
- 空白やNULLの扱い: 空は重複と扱わないのが無難。必要なら条件分岐で “空は除外”。
- 列順や見出しの変更: 列番号を固定せず、見出し検索(Find)で列を特定する運用に拡張可能。
- 大量データで遅い: Range→配列化、Dictionaryで存在判定、SpeedOn/Offの3点セットで解決。
