【保険】契約者の満年齢を正しく計算し、年齢区分別の件数と保険料を集計する

公開日:2026年10月7日 執筆:Takuya カテゴリ:業種別サンプル

保険の契約一覧から、基準日時点の契約者の満年齢を計算し、年齢区分ごとに件数と保険料の合計を集計するマクロです。DateDiff で年齢を出すと1歳ずれる落とし穴と、正しい計算方法を解説します。

想定する場面

保険の営業部門や商品企画では、保有契約を契約者の年齢層ごとに分けて、件数や保険料の構成を確認することがあります。たとえば「60代以上の契約が全体の何割か」「年齢区分ごとの平均保険料はいくらか」といった分析です。

このとき最初に必要になるのが、基準日時点の契約者の満年齢です。年齢の計算は簡単そうに見えますが、VBA の DateDiff 関数をそのまま使うと、誕生日前の人の年齢が1歳多くなるという落とし穴があります。

DateDiff で年齢を出してはいけない理由

DateDiff("yyyy", 生年月日, 基準日) は、2つの日付の間で「年が何回変わったか」を数える関数です。誕生日を迎えたかどうかは考慮しません。たとえば 1980年12月31日生まれの人の 2026年1月1日時点の満年齢は45歳ですが、DateDiff は年の境目を46回またいでいるので 46 を返します。

正しい満年齢は、「年の差」を出したうえで、基準日がその年の誕生日より前なら1を引く、という手順で計算します。月日の比較は、月日を4桁の文字列(mmdd)にして比べると簡単です。2月29日生まれの人も、うるう年でない年は3月1日から年齢が1つ増える扱いになり、日付単位の集計としては問題ありません。

データの形(例)

シートと列の構成は説明用の架空のものです。保険料は月払いの金額とし、集計では年額(12倍)に換算します。基準日は出力シートの B1 セルに入力しておきます。

  • 「契約一覧」シート:A列 証券番号、B列 契約者名、C列 生年月日、D列 商品名、E列 月払保険料、F列 契約状態(有効・失効など)
  • 「年齢区分別」シート:B1 セルに基準日。A3 セル以降にマクロが集計表を出力する

コード

契約状態が「有効」の契約だけを対象に、満年齢から年齢区分を決めて、件数と年換算保険料を足し込みます。区分の境目は配列 bounds で定義しているので、区分を変えたいときはそこだけ直します。金額は Currency 型で集計しています。

Sub SummaryByAgeBand()
    Dim ws As Worksheet, outWs As Worksheet, data As Variant
    Dim bounds As Variant, labels As Variant
    Dim cnt() As Long, prem() As Currency
    Dim baseDay As Date, lastRow As Long, i As Long, b As Long, age As Long, skipped As Long

    Set ws = ThisWorkbook.Worksheets("契約一覧")
    Set outWs = ThisWorkbook.Worksheets("年齢区分別")
    If Not IsDate(outWs.Range("B1").Value) Then MsgBox "B1 に基準日を入力してください": Exit Sub
    baseDay = CDate(outWs.Range("B1").Value)

    bounds = Array(0, 30, 40, 50, 60, 70)             ' 各区分の下限年齢
    labels = Array("29歳以下", "30代", "40代", "50代", "60代", "70歳以上")
    ReDim cnt(0 To UBound(bounds))
    ReDim prem(0 To UBound(bounds))

    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    If lastRow < 2 Then Exit Sub
    data = ws.Range("A2:F" & lastRow).Value

    For i = 1 To UBound(data, 1)
        If data(i, 6) = "有効" Then
            If IsDate(data(i, 3)) And IsNumeric(data(i, 5)) Then
                age = AgeAt(CDate(data(i, 3)), baseDay)
                For b = UBound(bounds) To 0 Step -1      ' 上の区分から当てはめる
                    If age >= bounds(b) Then Exit For
                Next b
                If b < 0 Then b = 0
                cnt(b) = cnt(b) + 1
                prem(b) = prem(b) + CCur(data(i, 5)) * 12
            Else
                skipped = skipped + 1                     ' 生年月日・保険料が不正
            End If
        End If
    Next i

    outWs.Range("A3:D" & outWs.Rows.Count).Clear
    outWs.Range("A3:D3").Value = Array("年齢区分", "件数", "年換算保険料計", "平均(年額)")
    For b = 0 To UBound(bounds)
        outWs.Cells(b + 4, 1).Value = labels(b)
        outWs.Cells(b + 4, 2).Value = cnt(b)
        outWs.Cells(b + 4, 3).Value = prem(b)
        If cnt(b) > 0 Then outWs.Cells(b + 4, 4).Value = prem(b) / cnt(b)
    Next b
    outWs.Range("C4:D" & UBound(bounds) + 4).NumberFormatLocal = "#,##0"
    MsgBox "集計しました。データ不備で対象外:" & skipped & " 件"
End Sub

' 基準日時点の満年齢
Function AgeAt(ByVal birth As Date, ByVal baseDay As Date) As Long
    Dim a As Long
    a = Year(baseDay) - Year(birth)
    If Format(baseDay, "mmdd") < Format(birth, "mmdd") Then a = a - 1   ' 誕生日前
    AgeAt = a
End Function

コードのポイントと注意点

AgeAt 関数はワークシートからも呼び出せるので、契約一覧の空いている列に「=AgeAt(C2, 基準日のセル)」と入れて、マクロの計算結果を1件ずつ確かめることもできます。誕生日の前日・当日・翌日のデータを用意して、年齢が切り替わるタイミングを確認しておくと安心です。

保険の実務では、満年齢ではなく「保険年齢」(1年未満の端数が6か月を超えると切り上げる数え方)を使う商品もあります。どちらの年齢で区分するかは分析の目的や商品の約款によって異なるため、集計の前に確認してください。

  • DateDiff("yyyy", …) は誕生日を考慮しないので、満年齢の計算には使わない
  • 生年月日が文字列で入っている場合は、先に日付型へ変換する
  • 年換算は月払いを前提にしているため、年払い・一時払いの契約が混在する場合は払込方法の列で換算を分ける
  • 契約者の個人情報を含むため、集計結果だけを共有し、明細は社内ルールに沿って扱う

※年齢区分・列構成は説明用の架空の例です。集計結果は社内の分析用の目安としてお使いください。

ほかの記事