Excel VBA 逆引き集 | 処理対象列を自動判定

Excel VBA
スポンサーリンク

ねらい:処理対象列を「自動判定」して人手を減らす

毎回「この列を処理する」と手で指定していませんか?大規模や可変フォーマットのシートでは、列位置が変わる、ヘッダー名が違う、空列が混ざる…などでミスが増えます。そこで、ヘッダー名やデータの特徴から「処理対象列を自動判定」するテンプレートを用意します。初心者でも貼って使える形で、重要ポイントを深掘りして解説します。

  • 目的: 列位置に依存しない堅牢な処理(ヘッダー名・部分一致・型判定・優先度)。
  • 考え方: 1) 候補列を抽出 → 2) ルールでスコア付け → 3) 最適列を採用 → 4) 例外時はフォールバック。
  • 重要ポイント(深掘り): ヘッダーで決め打ちせず、複数ルールを組み合わせると変化に強い。エラー時には「見つからなかった」ログ・ガードを必ず入れる。

基本テンプレ:ヘッダー名で確定(完全一致)

最もシンプルな方法。ヘッダー行から列番号を見つけます。

Function FindColumnByHeader(ByVal ws As Worksheet, ByVal headerRow As Long, ByVal headerName As String) As Long
    Dim lastCol As Long: lastCol = ws.Cells(headerRow, ws.Columns.Count).End(xlToLeft).Column
    Dim c As Long
    For c = 1 To lastCol
        If CStr(ws.Cells(headerRow, c).Value) = headerName Then
            FindColumnByHeader = c
            Exit Function
        End If
    Next
    FindColumnByHeader = 0 ' 見つからない場合
End Function

Sub Example_FindExact()
    Dim ws As Worksheet: Set ws = Worksheets("Input")
    Dim colEmpNo As Long: colEmpNo = FindColumnByHeader(ws, 1, "社員番号")
    If colEmpNo = 0 Then
        MsgBox "社員番号列が見つかりません。"
        Exit Sub
    End If
    MsgBox "社員番号列は " & Split(ws.Cells(1, colEmpNo).Address(0, 0), "$")(0) & " 列です。"
End Sub
VB
  • 深掘り: 完全一致は速くて堅いが、表記ゆれや別名(例:社員ID、従業員番号)に弱い。実務では次の部分一致・同義語セットまで用意すると安定する。

応用テンプレ:部分一致+同義語(ゆれ吸収)

複数候補名で最適列を探します。最初に見つかったものを採用するか、優先順位をつけます。

Function FindColumnByAliases(ByVal ws As Worksheet, ByVal headerRow As Long, ByRef aliases As Variant) As Long
    Dim lastCol As Long: lastCol = ws.Cells(headerRow, ws.Columns.Count).End(xlToLeft).Column
    Dim c As Long, i As Long, head As String
    For c = 1 To lastCol
        head = CStr(ws.Cells(headerRow, c).Value)
        For i = LBound(aliases) To UBound(aliases)
            If head Like "*" & aliases(i) & "*" Then
                FindColumnByAliases = c
                Exit Function
            End If
        Next
    Next
    FindColumnByAliases = 0
End Function

Sub Example_FindAliases()
    Dim ws As Worksheet: Set ws = Worksheets("Input")
    Dim names As Variant: names = Array("社員番号", "社員ID", "従業員番号")
    Dim col As Long: col = FindColumnByAliases(ws, 1, names)
    If col = 0 Then
        MsgBox "社員番号系の列が見つかりません。"
    Else
        MsgBox "対象列は " & Split(ws.Cells(1, col).Address(0, 0), "$")(0) & " 列です。"
    End If
End Sub
VB
  • 深掘り: 「部分一致」は柔軟だが、誤検知が起き得る(例:備考に“番号表記について”)。後述の型判定やサンプルチェックと組み合わせると精度が上がる。

信頼度アップ:サンプル行の型でスコアリング

ヘッダーだけでなく、複数行のデータ形(全部数字、日付が多い、文字長が一定)でスコア評価し、最も条件を満たす列を選びます。

Function GuessBestNumericColumn(ByVal ws As Worksheet, ByVal headerRow As Long, _
                                ByVal startRow As Long, ByVal endRow As Long) As Long
    Dim lastCol As Long: lastCol = ws.Cells(headerRow, ws.Columns.Count).End(xlToLeft).Column
    Dim c As Long, r As Long, score As Long, bestScore As Long, bestCol As Long
    bestScore = -1
    
    For c = 1 To lastCol
        score = 0
        For r = startRow To endRow
            Dim v As Variant: v = ws.Cells(r, c).Value
            If Len(CStr(v)) = 0 Then GoTo ContinueRow
            If IsNumeric(v) Then
                ' 整数なら加点
                If CDbl(v) = Int(CDbl(v)) Then score = score + 2 Else score = score + 1
            Else
                ' 非数なら減点
                score = score - 1
            End If
ContinueRow:
        Next
        If score > bestScore Then
            bestScore = score
            bestCol = c
        End If
    Next
    GuessBestNumericColumn = bestCol
End Function

Sub Example_GuessNumeric()
    Dim ws As Worksheet: Set ws = Worksheets("Input")
    Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    Dim sampleEnd As Long: sampleEnd = WorksheetFunction.Min(lastRow, 101) ' 100行サンプル
    
    Dim col As Long: col = GuessBestNumericColumn(ws, 1, 2, sampleEnd)
    MsgBox "数値っぽい列の推定結果: " & Split(ws.Cells(1, col).Address(0, 0), "$")(0)
End Sub
VB
  • 深掘り(重要):
    • サンプル数は「多すぎず少なすぎず」。100〜200行で十分なことが多い。
    • スコア設計はルール次第。例えば「電話番号は10〜11桁の数字」「社員番号は6桁の数字」など、業務ルールで加点条件を追加すると精度が劇的に上がる。

総合テンプレ:ヘッダー候補+型スコア+優先順位

複数の判定を合成して「最も妥当」な列を選ぶフレーム。

Function AutoDetectColumn(ByVal ws As Worksheet, ByVal headerRow As Long, _
                          ByVal startRow As Long, ByVal endRow As Long, _
                          ByRef aliases As Variant, ByVal expectLen As Long, _
                          Optional ByVal preferHeader As Boolean = True) As Long
    Dim lastCol As Long: lastCol = ws.Cells(headerRow, ws.Columns.Count).End(xlToLeft).Column
    Dim c As Long, head As String
    Dim bestCol As Long: bestCol = 0
    Dim bestScore As Long: bestScore = -100000
    
    For c = 1 To lastCol
        head = CStr(ws.Cells(headerRow, c).Value)
        
        ' 基本スコア(ヘッダー一致)
        Dim score As Long: score = 0
        Dim i As Long
        For i = LBound(aliases) To UBound(aliases)
            If head Like "*" & aliases(i) & "*" Then
                score = score + IIf(preferHeader, 50, 20) ' ヘッダー優先度
                Exit For
            End If
        Next
        
        ' データスコア(サンプル判定)
        Dim r As Long
        For r = startRow To endRow
            Dim v As Variant: v = ws.Cells(r, c).Value
            If Len(CStr(v)) = 0 Then GoTo ContinueRow
            If IsNumeric(v) Then
                ' 桁数が期待に近いほど加点(社員番号など)
                Dim s As String: s = CStr(v)
                If Len(s) = expectLen Then
                    score = score + 5
                ElseIf Abs(Len(s) - expectLen) <= 2 Then
                    score = score + 2
                Else
                    score = score + 1
                End If
            Else
                score = score - 2
            End If
ContinueRow:
        Next
        
        If score > bestScore Then
            bestScore = score
            bestCol = c
        End If
    Next
    
    AutoDetectColumn = bestCol
End Function

Sub Example_AutoDetect_EmployeeNo()
    Dim ws As Worksheet: Set ws = Worksheets("Input")
    Dim lastRow As Long: lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    Dim sampleEnd As Long: sampleEnd = WorksheetFunction.Min(lastRow, 102)
    Dim aliases As Variant: aliases = Array("社員番号", "社員ID", "従業員番号")
    
    Dim col As Long: col = AutoDetectColumn(ws, 1, 2, sampleEnd, aliases, 6, True)
    If col = 0 Then
        MsgBox "対象列が自動判定できませんでした。"
    Else
        MsgBox "自動判定した列: " & Split(ws.Cells(1, col).Address(0, 0), "$")(0) & "(" & ws.Cells(1, col).Value & ")"
    End If
End Sub
VB
  • 深掘り(重要):
    • 「ヘッダー名の信頼度」「データ形の信頼度」を重みづけして合計スコア→最良列。
    • 誤検知対策として「最低スコア閾値」を設け、閾値未満なら「未判定」と扱うのも有効。
    • 返却後は必ずガード(0ならエラー扱い)+ログ出力で原因特定をしやすく。

型別テンプレ:電話番号・日付・金額などの特徴で判定

用途別に特徴量を変えると精度が上がります。

Function ScorePhoneColumn(ByVal ws As Worksheet, ByVal c As Long, ByVal startRow As Long, ByVal endRow As Long) As Long
    Dim r As Long, score As Long
    For r = startRow To endRow
        Dim s As String: s = CStr(ws.Cells(r, c).Value)
        If Len(s) = 0 Then GoTo NextRow
        ' 数字+ハイフン可、数字抽出後10-11桁が望ましい
        Dim digits As String: digits = ExtractDigits(s)
        If Len(digits) >= 10 And Len(digits) <= 11 And digits Like "*[!0-9]*" = False Then
            score = score + 3
        Else
            score = score - 1
        End If
NextRow:
    Next
    ScorePhoneColumn = score
End Function

Private Function ExtractDigits(ByVal s As String) As String
    Dim i As Long, r As String
    For i = 1 To Len(s)
        Dim ch As String: ch = Mid$(s, i, 1)
        If ch Like "[0-9]" Then r = r & ch
    Next
    ExtractDigits = r
End Function
VB
  • 深掘り: 型に合わせて「期待桁数」「文字種」を評価する。日付なら IsDate を使って加点、金額なら「カンマ除去→IsNumeric→範囲」などを織り込む。

ヘッダー行が不明な時:上から数行を走査して推定

実務ではヘッダー行が1行目とは限りません。見出し行を自動で見つけます。

Function DetectHeaderRow(ByVal ws As Worksheet, ByVal maxCheckRows As Long) As Long
    Dim r As Long, c As Long, lastCol As Long
    For r = 1 To maxCheckRows
        lastCol = ws.Cells(r, ws.Columns.Count).End(xlToLeft).Column
        Dim headerCount As Long: headerCount = 0
        For c = 1 To lastCol
            Dim s As String: s = CStr(ws.Cells(r, c).Value)
            ' ヘッダーっぽい条件例:文字のみ・短い・記号少なめ
            If Len(s) > 0 And Len(s) <= 20 And s Like "*[A-Za-z0-9ぁ-んァ-ン一-龥]*" Then
                headerCount = headerCount + 1
            End If
        Next
        If headerCount >= WorksheetFunction.Max(1, lastCol \ 2) Then
            DetectHeaderRow = r
            Exit Function
        End If
    Next
    DetectHeaderRow = 1 ' フォールバック
End Function
VB
  • 深掘り: 「ヘッダーっぽさ」をルールで判定。厳密ではないが、可変フォーマットに対して自動調整の入口になります。決め打ちと併用がベスト。

例題で練習(貼って試せる)

  • 例1(完全一致): ヘッダー「社員番号」で列を取得し、該当列にフラグを書き込む。
  • 例2(同義語): ヘッダーが「社員ID」でも検出されるか確認。
  • 例3(型スコア): 6桁整数の社員番号列を推定し、結果を表示。
  • 例4(電話列): ハイフン混在の電話列をスコア判定し、最もスコアが高い列を選ぶ。
  • 例5(ヘッダー自動検出): 上から5行走査し、見出し行を推定→その行で列検出。

実務の落とし穴と対策(重要ポイント)

  • 落とし穴1:誤検知で別列を処理してしまう
    • 対策: ヘッダー+型スコアの併用、最低スコア閾値、検出結果を一度メッセージで確認・ログ記録。
  • 落とし穴2:空データや混在データでスコアが偏る
    • 対策: サンプル行数を増やす・欠損はスコアに影響しすぎないように設計(0点扱い等)。
  • 落とし穴3:列数が非常に多いと遅い
    • 対策: ヘッダー候補で列を絞ってから型スコアを適用。配列で一括読み(Value2)で高速化。
  • 落とし穴4:表記ゆれ(漢字/カナ/英語)
    • 対策: 同義語を十分に用意、部分一致にする、正規化(全角→半角・Trim)をヘッダーに適用。
  • 落とし穴5:ヘッダー行が変動
    • 対策: DetectHeaderRowで推定し、確信度が低い場合はユーザーに確認ダイアログ(運用設計で補う)。

スターター手順

  1. 完全一致の基本関数(FindColumnByHeader)でヘッダー列取得を体感。
  2. 同義語・部分一致(FindColumnByAliases)で表記ゆれ耐性を追加。
  3. 型スコア(AutoDetectColumn)で信頼度を上げる。
  4. ヘッダー自動検出(DetectHeaderRow)を組み合わせ、可変フォーマットへ対応。
  5. 結果ガード+ログを必ず入れて、誤検知時にすぐ気づける仕組みを整える。
タイトルとURLをコピーしました