VBAで自動化 VBA一覧 比較 配列・データ操作

【VBA】Min関数で最小値を取得する方法|変数・配列対応

売上・在庫・工数・納期など、実務データを扱っていると「最小値」を取りたい場面はかなり多いです。
一方で、最小値は最大値より事故が起きやすく、0になってしまうマイナスが混ざると崩れる配列だと取れないといったトラブルがよく起きます。
原因はだいたい決まっていて、初期値の置き方と、空白・文字・エラー値を想定しない設計です。
この記事では、Range(セル範囲)から最小値を取る基本から、配列(1次元・2次元)で最小値を安全に取る方法まで、実務で壊れにくい形でまとめます。

✅ VBAで最小値を取る方法は大きく3つ

最小値の取り方は、データの置き場所(Rangeか配列か)で変えるのが安全です。
ここを曖昧にすると「手元のサンプルでは動くけど、現場データで止まる」になりがちです。
特に配列は、WorksheetFunctionにそのまま渡せない/渡せても型や混在データで挙動が怪しくなります。
また、Rangeに対してWorksheetFunction.Minを使う場合でも、エラー値混入で落ちることがあります。
この章で全体像を押さえると、後半の設計意図が腑に落ちます。
結論として、RangeはMin関数、配列は走査(ループ)が実務では安定します。

・3パターンの使い分け(Range / 配列 / 高速化)

  • Rangeの最小値WorksheetFunction.Min / Application.Min
  • 配列の最小値:原則は ループで走査(混在データに強い)
  • 大量データ:Rangeを配列に読み込んで ループで最小値(高速)

✅ Rangeの最小値を変数に取得する基本

Rangeから最小値を取るのは一見簡単ですが、実務では「空白」「文字」「エラー値」「0の意味」が絡むと判断ミスが起きます。
初心者がよくやる失敗は、Minが取れたからOKと思ってしまい、実は空白や文字を含んだ範囲で結果が歪むケースです。
また WorksheetFunction.Min は、範囲に #N/A などが混ざると実行時エラーになり、処理が止まります。
さらに、最小値を Long に入れて小数が切り捨てられ、帳票の値がズレる事故もあります。
ここでは「壊れにくい基本形」を押さえた上で、落とし穴と対策も示します。

・WorksheetFunction.MinでRangeから最小値を取る

操作手順(実装の流れ)

  1. 最小値を取りたい範囲(Range)を用意する
  2. 受け取る変数は基本 Double にする
  3. WorksheetFunction.Min(範囲) を代入する
  4. エラー混入があり得るなら設計を切り替える(後述)
Option Explicit

Public Sub GetMinFromRange_Basic()
    Dim targetRange As Range
    Dim minValue As Double
    
    Set targetRange = ThisWorkbook.Worksheets("Sheet1").Range("B2:B100")
    
    ' Range内の最小値を取得(エラー値が混ざると落ちる可能性あり)
    minValue = Application.WorksheetFunction.Min(targetRange)
    
    Debug.Print "Min=", minValue
End Sub

なぜこの書き方か(設計意図)

  • Rangeが対象ならExcel関数を使うのが短くて読みやすい
  • Double にすることで小数や大きな数値でも安定しやすい

別案との違い

  • Long は小数が落ち、比較が崩れる原因になる
  • Variant は後続の比較処理で型がブレやすい

・エラー混入が怖いなら Application.Min も候補(ただし万能ではない)

Application.Min は落ちにくいことがありますが、入力の混在状況によって結果が読みづらくなることもあるため、「エラー混入の可能性があるRange」は配列化→ループの方が堅いです。

Option Explicit

Public Sub GetMinFromRange_ApplicationMin()
    Dim targetRange As Range
    Dim resultVariant As Variant
    
    Set targetRange = ThisWorkbook.Worksheets("Sheet1").Range("B2:B100")
    
    resultVariant = Application.Min(targetRange)
    
    Debug.Print "Min=", resultVariant
End Sub

✅ 配列の最小値を取得する基本設計

ここがつまずきポイントです。
RangeならMin関数で一発でも、配列はそのまま同じ発想で扱えません。
さらに配列は Empty、文字列、Nullのような“混ざり”が起きやすく、比較の前提が崩れます。
実務で壊れない形にするには、「数値だけを対象に最小値を更新する」というルールを固定します。
また「最小値が存在しない(全部空白や文字)」ケースを、0やFalseで誤魔化すと誤認の原因になります。
ここでは Try~ 形式で、見つかったかどうかを返す設計にします。

・配列最小値は「走査して更新」が一番堅い

操作手順(実装の流れ)

  1. 最小値の初期状態(未確定)を用意する
  2. 配列を先頭から走査する
  3. 数値だけを対象に比較する
  4. 小さければ最小値を更新する
  5. 最後に「最小値が見つかったか」を返す

✅ 1次元配列の最小値を安全に取る関数

最小値取得は関数化しておくと、Min/Maxやしきい値判定に流用できます。
場当たりのFor文は増えやすく、後から仕様変更(例:文字は除外、0は除外、負数は除外など)が入った瞬間に崩れます。
ここでは「数値だけ対象」「最小値が存在しない場合はFalse」を基本にします。
これにより、空データでも誤って0を最小値と認識しにくくなります。
“落ちない”だけでなく、“誤解しない”ことが重要です。

・関数:Variant配列から最小値を取得(数値のみ対象)

Option Explicit

Public Function TryGetMinFrom1DArray( _
    ByVal values As Variant, _
    ByRef minValue As Double _
) As Boolean
    
    Dim i As Long
    Dim hasNumber As Boolean
    Dim currentValue As Double
    
    If Not IsArray(values) Then Exit Function
    
    hasNumber = False
    
    For i = LBound(values) To UBound(values)
        If IsNumeric(values(i)) Then
            currentValue = CDbl(values(i))
            
            If Not hasNumber Then
                minValue = currentValue
                hasNumber = True
            ElseIf currentValue < minValue Then
                minValue = currentValue
            End If
        End If
    Next i
    
    TryGetMinFrom1DArray = hasNumber
End Function

なぜこの書き方か(設計意図)

  • Try~ にして、最小値が存在しないケースを表現できる
  • 数値以外を除外して、混在データでも壊れにくくする
  • Double に寄せて小数・負数を含む実務データに対応する

別の書き方との差(メリット)

  • 初期値を0にすると、最小値が正しく取れない(例えば全て正の値でも0が最小になってしまう)
  • WorksheetFunction.Min に無理に渡すより挙動が明確でテストしやすい

・呼び出し例:最小値を変数に入れて使う

Option Explicit

Public Sub Sample_MinFrom1DArray()
    Dim values(1 To 6) As Variant
    Dim minValue As Double
    Dim success As Boolean
    
    values(1) = 10
    values(2) = "15"
    values(3) = Empty
    values(4) = -3
    values(5) = "NG"
    values(6) = 22.5
    
    success = TryGetMinFrom1DArray(values, minValue)
    
    If success Then
        Debug.Print "Min=", minValue
    Else
        Debug.Print "数値が1件もありません。"
    End If
End Sub

✅ 2次元配列の最小値を取る(Range取り込み後の定番)

実務で多いのは、Rangeを配列に取り込んで処理するパターンです。
このとき配列は2次元(行×列)になり、1次元のMin関数はそのまま使えません。
ここで列ごとに切り出すような実装にすると、コードが散らかりがちです。
2次元は2次元として走査し、対象列だけ比較する形にすると読みやすく保守もしやすいです。
また、Range→配列化は高速化に直結するため、データが増えるほど効果が出ます。
ここでは、取り込みから最小値取得までを“定番形”としてまとめます。

・関数:2次元配列の特定列から最小値を取得

Option Explicit

Public Function TryGetMinFrom2DArrayColumn( _
    ByVal values As Variant, _
    ByVal targetColumnIndex As Long, _
    ByRef minValue As Double _
) As Boolean
    
    Dim rowIndex As Long
    Dim hasNumber As Boolean
    Dim currentValue As Double
    
    If Not IsArray(values) Then Exit Function
    
    hasNumber = False
    
    For rowIndex = LBound(values, 1) To UBound(values, 1)
        If IsNumeric(values(rowIndex, targetColumnIndex)) Then
            currentValue = CDbl(values(rowIndex, targetColumnIndex))
            
            If Not hasNumber Then
                minValue = currentValue
                hasNumber = True
            ElseIf currentValue < minValue Then
                minValue = currentValue
            End If
        End If
    Next rowIndex
    
    TryGetMinFrom2DArrayColumn = hasNumber
End Function

・呼び出し例:Rangeを配列に読み込んで最小値(高速・安定)

Option Explicit

Public Sub Sample_MinFromRangeByArray()
    Dim ws As Worksheet
    Dim dataRange As Range
    Dim dataValues As Variant
    
    Dim minValue As Double
    Dim success As Boolean
    
    Set ws = ThisWorkbook.Worksheets("Sheet1")
    Set dataRange = ws.Range("A2:D100") ' 例:A=日付, B=担当, C=売上, D=粗利
    
    dataValues = dataRange.Value
    
    ' 例:C列(3列目)の最小値を取得
    success = TryGetMinFrom2DArrayColumn(dataValues, 3, minValue)
    
    If success Then
        Debug.Print "売上最小=", minValue
    Else
        Debug.Print "数値が見つかりません。"
    End If
End Sub

 

ここでは、Rangeを配列に取り込んでから
2次元配列の最小値を取得する方法を紹介しました。

同じ考え方で、最大値を取得する処理も実装できます。
売上の最高値や最大工数など、実務では最大値を取得する場面も非常に多くあります。

変数・配列の両方に対応した最大値の取得方法については、
次の記事で詳しく解説しています。

👉 【VBA】Max関数で最大値を取得する方法|変数・配列対応


 

✅ 実務でありがちな落とし穴と対策

MinはMaxより「初期値の置き方」が難しいです。
雑に書くと、最小値が0に固定されたり、空データで誤判定したりします。
また、0が“有効な値”なのか“欠損の代替”なのかで判断が変わります。
このような曖昧さを残すと、月末や例外データで事故になります。
ここでは、現場でよくある落とし穴と対策を整理します。

・初期値を0にして最小値を更新する(ほぼNG)

  • 正のデータしかない場合、最小値が0になりやすい
  • データが空でも0になり、成功と失敗を区別できない

→ 対策:hasNumber フラグで「最小値が存在するか」を分ける(本記事の方式)

・型がLongで小数が落ちる

  • 単価・比率・平均などで小数が混ざる
  • Long に入れて丸められ、比較がズレる

→ 対策:最小値は基本 Double、必要なら最後に丸める

・Rangeにエラー値が混ざってWorksheetFunction.Minで落ちる

  • #N/A#DIV/0! が混ざる
  • 特定の月や特定部署だけで止まる

→ 対策:Range→配列化→数値だけ比較、の流れに寄せる

実務では、最小値を取得したときに
0が最小値として取得されてしまうケースも少なくありません。

例えば

  • 空白セルを0として扱ってしまう

  • 初期値の設定によって0が最小値になる

  • 実際には0を除外して最小値を取りたい

といったケースです。

このように0を除外して最小値を取得する方法については、
次の記事で詳しく解説しています。

👉 【VBA】Min関数の最小値を0以外にする方法


✅ 応用:最小値だけでなく「最小の行」を取りたい

実務では、最小値を取った後に「その行の担当者」や「案件名」を取りたいことが多いです。
例えば、最小売上の担当のフォローが必要、最小在庫の商品を発注したい、などです。
この場合、最小値だけ取って終わると、また検索し直すことになります。
最小値の更新と同時に行インデックスも保持しておけば、1回の走査で完結します。
処理が速くなり、コードも読みやすくなります。

・関数:2次元配列で最小値と行インデックスを同時に取得

Option Explicit

Public Function TryGetMinWithRowIndexFrom2DArray( _
    ByVal values As Variant, _
    ByVal targetColumnIndex As Long, _
    ByRef minValue As Double, _
    ByRef minRowIndex As Long _
) As Boolean
    
    Dim rowIndex As Long
    Dim hasNumber As Boolean
    Dim currentValue As Double
    
    If Not IsArray(values) Then Exit Function
    
    hasNumber = False
    
    For rowIndex = LBound(values, 1) To UBound(values, 1)
        If IsNumeric(values(rowIndex, targetColumnIndex)) Then
            currentValue = CDbl(values(rowIndex, targetColumnIndex))
            
            If Not hasNumber Then
                minValue = currentValue
                minRowIndex = rowIndex
                hasNumber = True
            ElseIf currentValue < minValue Then
                minValue = currentValue
                minRowIndex = rowIndex
            End If
        End If
    Next rowIndex
    
    TryGetMinWithRowIndexFrom2DArray = hasNumber
End Function

・呼び出し例:最小売上の担当者を取得

Option Explicit

Public Sub Sample_MinValueAndOwner()
    Dim ws As Worksheet
    Dim dataRange As Range
    Dim dataValues As Variant
    
    Dim minValue As Double
    Dim minRowIndex As Long
    Dim success As Boolean
    
    Dim ownerName As String
    
    Set ws = ThisWorkbook.Worksheets("Sheet1")
    Set dataRange = ws.Range("A2:D100") ' B列=担当, C列=売上想定
    dataValues = dataRange.Value
    
    success = TryGetMinWithRowIndexFrom2DArray(dataValues, 3, minValue, minRowIndex)
    
    If Not success Then
        Debug.Print "数値が見つかりません。"
        Exit Sub
    End If
    
    ownerName = CStr(dataValues(minRowIndex, 2))
    Debug.Print "最小売上=", minValue, "担当=", ownerName
End Sub

✅ 最小値は「異常検知」や「下限チェック」に使える

最小値は、最大値よりも「異常検知」に向いています。
例えば、工数が0になっている、在庫がマイナスになっている、単価が極端に低い、などです。
こうした異常は、平均や合計では埋もれがちです。
最小値を取る処理が安定していると、チェック自動化が一段進みます。
今回の TryGetMin~ 形式は、そのまま「0を除外する最小値」や「負数だけの最小値」などへ拡張できます。
“動く”だけでなく“運用できる”形にしておくと、後から効いてきます。


 

✅ まとめ:VBAでMinを安全に扱い最小値を正しく取る

  • Rangeの最小値は WorksheetFunction.Min が手早いが、エラー混入には注意
  • 配列の最小値は 走査して更新が最も堅く、混在データに強い
  • 初期値0方式は誤判定が多いので、hasNumber で「存在」を分ける
  • 2次元配列は「対象列だけ比較」でシンプルに実装できる
  • 最小値と一緒に「最小の行」も取ると、実務の抽出やフォローが一気に楽になる

最小値取得は、集計だけでなく 異常検知・チェック自動化にも直結する基礎部品です。
Maxとセットで設計しておくと、比較カテゴリの一連の処理が“壊れない部品”として揃っていきます。

    -VBAで自動化, VBA一覧, 比較, 配列・データ操作