Excel VBA | 複雑な条件を扱う方法

VBA
スポンサーリンク

複雑な条件は「一気に全部を判定しない」ことがコツです。大事なのは分割・標準化・見える化。条件を小さく切って順番に確定し、最後に組み合わせて結果を出すと、初心者でも迷わず書けます。


基本戦略(これだけ覚えれば大丈夫)

  • 分割: 大きな判定を、意味のある小さな判断に分ける
  • 標準化: 比較前に文字や数値の形式をそろえる(大小・空白・範囲)
  • 早期終了: 想定外やエラーは最初に弾いて、後続の条件をシンプルにする
  • 段階化: 「軸ごと」に判定を進める(例:カテゴリ→金額→方法)
  • 見える化: ルールを表や関数に移し、コードの分岐を減らす

例1: 巨大な If を「分割+段階化」で読みやすく

シナリオ

「会員種別 × 金額 × 支払い方法」で手数料を決める。最初は If の海になりがちです。

改善後(Select Case+段階化)

Function CalcFee(memberType As String, amount As Currency, pay As String) As Currency
    ' 標準化(大小・空白)
    memberType = UCase(Trim(memberType))
    pay = UCase(Trim(pay))

    ' 早期終了(エラー・想定外)
    If amount <= 0 Then CalcFee = 0: Exit Function
    If pay <> "CARD" And pay <> "CASH" Then CalcFee = 1000: Exit Function

    ' 段階化(金額帯を先に決める)
    Dim band As String
    If amount >= 100000 Then
        band = "HIGH"
    ElseIf amount >= 20000 Then
        band = "MID"
    Else
        band = "LOW"
    End If

    ' 外側:会員種別ごとのルールブロック
    Select Case UCase(memberType)
        Case "VIP"
            Select Case band
                Case "HIGH": CalcFee = IIf(pay = "CARD", 0, 200)
                Case "MID":  CalcFee = IIf(pay = "CARD", 100, 300)
                Case "LOW":  CalcFee = IIf(pay = "CARD", 200, 400)
            End Select
        Case "REG"
            Select Case band
                Case "HIGH": CalcFee = IIf(pay = "CARD", 300, 500)
                Case "MID":  CalcFee = IIf(pay = "CARD", 400, 600)
                Case "LOW":  CalcFee = IIf(pay = "CARD", 500, 700)
            End Select
        Case Else
            CalcFee = 1000
    End Select
End Function
VB
  • ポイント:
    • 標準化で比較ミスを減らす。
    • 早期終了で不正値の枝を消す。
    • 金額帯の段階化で内側の条件を簡潔にする。

例2: 「ひとつのキー」にまとめてフラット化

シナリオ

二段・三段のネストが長くなってきたら、前処理で「キー化」して1段の Select Case にします。

Function ShippingRule(region As String, weight As Double) As String
    region = UCase(Trim(region))

    ' ガード節
    If weight <= 0 Then ShippingRule = "重量不正": Exit Function

    ' キー化(組み合わせをひとつの値に)
    Dim weightBand As String
    If weight < 1 Then
        weightBand = "LIGHT"
    ElseIf weight < 5 Then
        weightBand = "MED"
    Else
        weightBand = "HEAVY"
    End If

    Dim key As String
    key = region & "_" & weightBand

    Select Case key
        Case "LOCAL_LIGHT":  ShippingRule = "当日配達"
        Case "LOCAL_MED":    ShippingRule = "翌日配達"
        Case "LOCAL_HEAVY":  ShippingRule = "通常配達"
        Case "REMOTE_LIGHT": ShippingRule = "航空便"
        Case "REMOTE_MED":   ShippingRule = "船便"
        Case "REMOTE_HEAVY": ShippingRule = "大型貨物"
        Case Else:           ShippingRule = "ルール未定義"
    End Select
End Function
VB
  • メリット:
    • 1段で完結して見通しが良い。
    • 組み合わせ追加が容易(Caseの行を増やすだけ)。

例3: ルールの「表」を使う(見える化)

シナリオ

条件が増えるほどコードが長くなる。ルールをワークシートに置いて、検索して使う方法。

' ルール表(シート "Rules")
' A:会員種別  B:金額帯  C:支払い方法  D:手数料
' 例: VIP | HIGH | CARD | 0

Function LookupFee(memberType As String, band As String, pay As String) As Currency
    Dim ws As Worksheet, lastRow As Long, i As Long
    Set ws = ThisWorkbook.Worksheets("Rules")
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

    memberType = UCase(Trim(memberType))
    band = UCase(Trim(band))
    pay = UCase(Trim(pay))

    For i = 2 To lastRow ' ヘッダ行を除く
        If ws.Cells(i, "A").Value = memberType _
           And ws.Cells(i, "B").Value = band _
           And ws.Cells(i, "C").Value = pay Then
            LookupFee = ws.Cells(i, "D").Value
            Exit Function
        End If
    Next i

    LookupFee = 1000 ' 未定義はデフォルト
End Function
VB
  • ポイント:
    • 業務ルールを表に移すと、コード変更なしで運用側が更新できる。
    • テストが楽(表を差し替えるだけ)。

例4: 真理値(True/False)を先に確定する

シナリオ

複雑な条件式は、意味のある「真偽フラグ」に分解してから組み立てる。

Function CanApplyCoupon(isMember As Boolean, isFirstPurchase As Boolean, amount As Currency, hasSpecialSale As Boolean) As Boolean
    ' フラグ分解
    Dim enoughAmount As Boolean: enoughAmount = (amount >= 5000)
    Dim specialOK As Boolean:    specialOK = (hasSpecialSale = False)

    ' 早期終了
    If Not isMember Then CanApplyCoupon = False: Exit Function

    ' 組み立て(読みやすい論理式)
    CanApplyCoupon = (isFirstPurchase Or specialOK) And enoughAmount
End Function
VB
  • メリット:
    • 長い条件式でも意味の単位で読める。
    • テストしやすい(フラグごとに検証)。

例5: 入力の標準化とガード節

シナリオ

文字入力に揺らぎ(全角・半角・大小・空白)があると条件ミスが起きる。

Function ParseDept(rawDept As String) As String
    Dim dept As String
    dept = UCase(Trim(rawDept))           ' 余白・大小を統一
    dept = Replace(dept, " ", "")        ' 全角スペース除去
    dept = Replace(dept, "DEPT-", "")     ' 接頭辞を除去

    Select Case dept
        Case "SALES": ParseDept = "営業"
        Case "HR":    ParseDept = "人事"
        Case "IT":    ParseDept = "情報システム"
        Case Else:    ParseDept = "不明"
    End Select
End Function
VB
  • ポイント:
    • 比較前の前処理で条件分岐を安定化。
    • 仕様変更に強い(前処理を直せば判定が保てる)。

よくあるつまずきと解決

  • 重なる範囲が混乱する:
    狭い条件→広い条件の順に並べる。Case Else は最後。
  • ネストが深くなりすぎる:
    キー化して1段にする、または表に移す
  • 同じ処理が重複:
    補助関数に切り出して再利用する。
  • 可読性が落ちる論理式:
    真偽フラグに分解してから組み立てる。
  • 入力の揺らぎで誤判定:
    Trim/UCase/Replaceで標準化してから比較。

小さな練習課題

  • 課題1: 「地域 × 会員種別 × 金額帯」でポイント倍率を返す関数。キー化でフラットに書いてください。
  • 課題2: 返品ルールをシートに表で作り、カテゴリ・日数・状態(未開封/開封済)で可否を検索する関数を書いてください。
  • 課題3: 入力文字列の正規化(余白・全角記号・大小)を行う補助関数 Normalize を作り、他の判定関数から必ず呼ぶようにしてください。

小さな練習課題の解答と解説

課題1: 「地域 × 会員種別 × 金額帯」でポイント倍率を返す関数(キー化でフラット化)

解答例

Function GetPointRate(region As String, memberType As String, amount As Currency) As Double
    region = UCase(Trim(region))
    memberType = UCase(Trim(memberType))

    ' 金額帯を先に決める
    Dim band As String
    If amount >= 100000 Then
        band = "HIGH"
    ElseIf amount >= 50000 Then
        band = "MID"
    Else
        band = "LOW"
    End If

    ' キー化(組み合わせをひとつの文字列に)
    Dim key As String
    key = region & "_" & memberType & "_" & band

    Select Case key
        Case "TOKYO_VIP_HIGH": GetPointRate = 3
        Case "TOKYO_VIP_MID":  GetPointRate = 2
        Case "TOKYO_VIP_LOW":  GetPointRate = 1
        Case "TOKYO_REG_HIGH": GetPointRate = 2
        Case "TOKYO_REG_MID":  GetPointRate = 1.5
        Case "TOKYO_REG_LOW":  GetPointRate = 1
        Case Else:             GetPointRate = 0.5 ' デフォルト
    End Select
End Function
VB

解説

  • 金額帯を先に分類して「HIGH/MID/LOW」に。
  • キー化で「地域会員種別金額帯」というひとつの文字列にまとめる。
  • Select Caseは1段で済み、見通しが良くなる。

課題2: 返品ルールをシートに表で作り、カテゴリ・日数・状態で可否を検索

解答例

(シート “ReturnRules” にルール表を作成:A列=カテゴリ、B列=日数上限、C列=状態、D列=可否)

Function CanReturn(category As String, days As Integer, status As String) As String
    Dim ws As Worksheet, lastRow As Long, i As Long
    Set ws = ThisWorkbook.Worksheets("ReturnRules")
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

    category = UCase(Trim(category))
    status = UCase(Trim(status))

    For i = 2 To lastRow ' ヘッダを除く
        If ws.Cells(i, "A").Value = category _
           And days <= ws.Cells(i, "B").Value _
           And ws.Cells(i, "C").Value = status Then
            CanReturn = ws.Cells(i, "D").Value
            Exit Function
        End If
    Next i

    CanReturn = "ルール未定義"
End Function
VB

解説

  • ルールをシートに置くことで、コードを変更せずに運用側が更新可能。
  • 検索ループで一致条件を探し、可否を返す。
  • days <= 上限で日数判定を行う。

課題3: 入力文字列の正規化(余白・全角記号・大小)を行う補助関数 Normalize

解答例

Function Normalize(text As String) As String
    Dim result As String
    result = Trim(text)                ' 前後の空白除去
    result = Replace(result, " ", "") ' 全角スペース除去
    result = UCase(result)             ' 大文字に統一
    Normalize = result
End Function

' 使用例
Function ParseDept(rawDept As String) As String
    Dim dept As String
    dept = Normalize(rawDept)

    Select Case dept
        Case "SALES": ParseDept = "営業"
        Case "HR":    ParseDept = "人事"
        Case "IT":    ParseDept = "情報システム"
        Case Else:    ParseDept = "不明"
    End Select
End Function
VB

解説

  • Normalize関数で入力を標準化。
  • Trim/UCase/Replaceを組み合わせて揺らぎを吸収。
  • 他の判定関数から必ず呼び出すことで、条件分岐が安定する。
タイトルとURLをコピーしました