【SQL】誕生日から年齢を算出する方法|MySQL・PostgreSQL・SQL Server・Oracle対応の計算テクニック

SQLで誕生日(生年月日)から年齢を計算する処理は、会員管理・顧客分析・年齢制限チェックなど、あらゆる業務システムで必要になります。しかし、RDBMS(データベース)ごとに使える関数が異なるため、正しい方法を知っておくことが重要です。

この記事では、MySQL・PostgreSQL・SQL Server・Oracle の4大RDBMSそれぞれでの年齢計算方法を基本から解説し、うるう年の落とし穴年代別グループ集計年齢フィルタリングなど実務で役立つパターンまで網羅します。

この記事で学べること

  • 年齢計算の基本的な考え方と注意点
  • MySQL:TIMESTAMPDIFF を使った年齢計算
  • PostgreSQL:AGE() 関数と EXTRACT の活用
  • SQL Server:DATEDIFF + 誕生日補正の正確な計算
  • Oracle:MONTHS_BETWEEN を使った年齢計算
  • うるう年(2/29生まれ)の扱いと境界値の落とし穴
  • 年齢でグループ分け(年代別集計)する方法
  • WHERE句で年齢フィルタリングする方法
  • RDBMS間の比較と使い分け
  • よくあるエラーと対処法
スポンサーリンク

サンプルデータ

この記事では、以下の users テーブルを使って解説します。さまざまな年齢・誕生日パターンを含めています。

id name birthday 備考
1 田中太郎 1990-05-15 一般的な日付
2 鈴木花子 2000-02-29 うるう年生まれ
3 佐藤一郎 1985-12-31 年末生まれ
4 高橋美咲 2005-01-01 年始生まれ
5 山田健太 1978-03-01 3月1日生まれ
6 伊藤恵 2010-07-20 未成年
サンプルデータの作成SQL(クリックで展開)
CREATE TABLE + INSERT
CREATE TABLE users (
    id       INT PRIMARY KEY,
    name     VARCHAR(50),
    birthday DATE
);

INSERT INTO users (id, name, birthday) VALUES
(1, '田中太郎', '1990-05-15'),
(2, '鈴木花子', '2000-02-29'),
(3, '佐藤一郎', '1985-12-31'),
(4, '高橋美咲', '2005-01-01'),
(5, '山田健太', '1978-03-01'),
(6, '伊藤恵',   '2010-07-20');

年齢計算の基本的な考え方

プログラムで年齢を計算する際、一見「現在の年 – 生まれた年」で求められそうに思えますが、誕生日がまだ来ていない場合は1を引く必要があります。

年齢計算のロジック(疑似コード)
-- 年齢 = 現在の年 - 誕生年
-- ただし、今年の誕生日がまだ来ていなければ -1

年齢 = (今日の年) - (誕生年)
IF 今日の月日 < 誕生月日 THEN
    年齢 = 年齢 - 1
END IF

各RDBMSには、この計算を正確に行う組み込み関数が用意されています。以下のセクションで、データベースごとの方法を見ていきましょう。

注意:「年 – 年」だけの単純計算は誕生日前後で1歳のズレが生じます。必ず月日まで考慮した計算方法を使いましょう。

MySQL:TIMESTAMPDIFF で年齢を計算する

MySQLでは TIMESTAMPDIFF 関数を使うことで、誕生日を月日レベルで正確に考慮した年齢計算ができます。

基本構文

MySQL
SELECT
    name,
    birthday,
    TIMESTAMPDIFF(YEAR, birthday, CURDATE()) AS age
FROM
    users;

実行結果(2026-03-05 時点)

name       | birthday   | age
-----------+------------+----
田中太郎   | 1990-05-15 |  35
鈴木花子   | 2000-02-29 |  26
佐藤一郎   | 1985-12-31 |  40
高橋美咲   | 2005-01-01 |  21
山田健太   | 1978-03-01 |  48
伊藤恵     | 2010-07-20 |  15

ポイント:TIMESTAMPDIFF(YEAR, 開始日, 終了日) は内部で月日を比較し、まだ誕生日が来ていなければ自動的に1を引いてくれるため、正確な満年齢が得られます。

TIMESTAMPDIFF の引数

引数 説明
YEAR 年単位の差(年齢計算に使用)
第2引数 開始日(= 誕生日)
第3引数 終了日(= CURDATE() で今日の日付)

特定日時点の年齢を求める

CURDATE() の代わりに任意の日付を指定すれば、過去や未来の特定日時点での年齢も算出できます。

MySQL – 特定日時点の年齢
-- 2025年4月1日時点の年齢を算出
SELECT
    name,
    birthday,
    TIMESTAMPDIFF(YEAR, birthday, '2025-04-01') AS age_at_date
FROM
    users;

PostgreSQL:AGE() 関数で年齢を計算する

PostgreSQLには AGE() という年齢計算の専用関数が用意されており、最もシンプルに年齢を取得できます。

AGE() 関数の基本

PostgreSQL – AGE() 関数
-- AGE() は interval 型で「○ years ○ mons ○ days」を返す
SELECT
    name,
    birthday,
    AGE(CURRENT_DATE, birthday) AS age_interval
FROM
    users;

実行結果

name       | birthday   | age_interval
-----------+------------+-------------------
田中太郎   | 1990-05-15 | 35 years 9 mons 18 days
鈴木花子   | 2000-02-29 | 26 years 4 days
佐藤一郎   | 1985-12-31 | 40 years 2 mons 2 days
...

年齢(整数)だけを取得する

AGE() の戻り値は interval 型なので、整数の年齢を取得するには EXTRACT または DATE_PART を組み合わせます。

PostgreSQL – EXTRACT + AGE()
-- 方法1: EXTRACT を使う
SELECT
    name,
    birthday,
    EXTRACT(YEAR FROM AGE(CURRENT_DATE, birthday)) AS age
FROM
    users;

-- 方法2: DATE_PART を使う(結果は同じ)
SELECT
    name,
    birthday,
    DATE_PART('year', AGE(CURRENT_DATE, birthday)) AS age
FROM
    users;

実行結果

name       | birthday   | age
-----------+------------+----
田中太郎   | 1990-05-15 |  35
鈴木花子   | 2000-02-29 |  26
佐藤一郎   | 1985-12-31 |  40
高橋美咲   | 2005-01-01 |  21
山田健太   | 1978-03-01 |  48
伊藤恵     | 2010-07-20 |  15

ポイント:AGE() に引数を1つだけ渡すと、自動的に CURRENT_DATE との差を計算します。AGE(birthday)AGE(CURRENT_DATE, birthday) は同じ結果です。

SQL Server:DATEDIFF + 誕生日補正で年齢を計算する

SQL Serverの DATEDIFF 関数は、単純に年の差を返すだけで月日を考慮しません。そのため、誕生日がまだ来ていない場合の補正が必要です。

DATEDIFF だけでは不正確

SQL Server – 不正確な例
-- NG: DATEDIFF だけでは誕生日前でも +1 されてしまう
SELECT
    name,
    birthday,
    DATEDIFF(YEAR, birthday, GETDATE()) AS wrong_age
FROM
    users;

注意:DATEDIFF(YEAR, '2000-12-31', '2001-01-01')1 を返します。たった1日の差でも年が変わると1年としてカウントされるため、年齢計算には使えません。

正確な年齢計算(誕生日補正付き)

SQL Server – 正確な年齢計算
SELECT
    name,
    birthday,
    DATEDIFF(YEAR, birthday, GETDATE())
    - CASE
        WHEN FORMAT(GETDATE(), 'MMdd')
           < FORMAT(birthday, 'MMdd')
        THEN 1
        ELSE 0
      END AS age
FROM
    users;

この方法では、FORMAT で月日を 'MMdd' 形式の文字列に変換し、今日の月日と誕生日の月日を比較します。今年の誕生日がまだ来ていなければ1を引くことで、正確な満年齢が得られます。

別の書き方(MONTH / DAY で比較)

SQL Server – MONTH/DAY を使う方法
SELECT
    name,
    birthday,
    DATEDIFF(YEAR, birthday, GETDATE())
    - CASE
        WHEN MONTH(GETDATE()) < MONTH(birthday)
          OR (MONTH(GETDATE()) = MONTH(birthday)
              AND DAY(GETDATE()) < DAY(birthday))
        THEN 1
        ELSE 0
      END AS age
FROM
    users;

Oracle:MONTHS_BETWEEN で年齢を計算する

Oracleでは MONTHS_BETWEEN 関数で2つの日付の月数差を求め、12で割って切り捨てることで年齢を算出します。

基本構文

Oracle
SELECT
    name,
    birthday,
    TRUNC(MONTHS_BETWEEN(SYSDATE, birthday) / 12) AS age
FROM
    users;

実行結果

NAME       | BIRTHDAY   | AGE
-----------+------------+----
田中太郎   | 1990-05-15 |  35
鈴木花子   | 2000-02-29 |  26
佐藤一郎   | 1985-12-31 |  40
高橋美咲   | 2005-01-01 |  21
山田健太   | 1978-03-01 |  48
伊藤恵     | 2010-07-20 |  15

ポイント:MONTHS_BETWEEN は日の部分も考慮して正確な月数差を返します。TRUNC(切り捨て)で小数部を除去することで、満年齢が得られます。FLOOR も同じ動作をしますが、TRUNC がOracle の慣用表現です。

EXTRACT を使った別の方法

Oracle – EXTRACT を使う方法
SELECT
    name,
    birthday,
    EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM birthday)
    - CASE
        WHEN TO_CHAR(SYSDATE, 'MMDD')
           < TO_CHAR(birthday, 'MMDD')
        THEN 1
        ELSE 0
      END AS age
FROM
    users;

RDBMS別 年齢計算 比較一覧

4つのRDBMSでの年齢計算方法を比較表にまとめます。

RDBMS 推奨方法 月日考慮 補足
MySQL TIMESTAMPDIFF(YEAR, birthday, CURDATE()) 自動 最もシンプル。補正不要
PostgreSQL EXTRACT(YEAR FROM AGE(birthday)) 自動 AGE()が専用関数で便利
SQL Server DATEDIFF(YEAR,...) - CASE補正 手動 DATEDIFF単独はNG。補正必須
Oracle TRUNC(MONTHS_BETWEEN(...)/12) 自動 MONTHS_BETWEENが日も考慮

年齢計算の落とし穴:うるう年(2月29日生まれ)

年齢計算で最も注意が必要なのがうるう年の2月29日生まれです。うるう年でない年には2月29日が存在しないため、各RDBMSでの扱いを理解しておく必要があります。

2000年2月29日生まれの場合

判定日 MySQL PostgreSQL SQL Server Oracle
2025-02-28 24 24 24(補正あり) 24
2025-03-01 25 25 25(補正あり) 25
2028-02-28 27 27 27(補正あり) 27
2028-02-29 28 28 28(補正あり) 28

注意:日本の法律(年齢計算ニ関スル法律)では、2月29日生まれの人はうるう年でない年は2月28日の終了時(3月1日の0時)に加齢します。MySQL の TIMESTAMPDIFF、PostgreSQL の AGE()、Oracle の MONTHS_BETWEEN はこの挙動と一致しますが、SQL Server の DATEDIFF は手動補正が必要なので特に注意してください。

うるう年を考慮した検証SQL(MySQL)

MySQL – うるう年境界値テスト
-- 2000-02-29 生まれの人の年齢を各日付で確認
SELECT
    test_date,
    TIMESTAMPDIFF(YEAR, '2000-02-29', test_date) AS age
FROM (
    SELECT '2025-02-28' AS test_date
    UNION ALL SELECT '2025-03-01'
    UNION ALL SELECT '2028-02-28'
    UNION ALL SELECT '2028-02-29'
) dates;

年齢でグループ分け(年代別集計)

マーケティングや統計分析では、年齢を10歳刻みの年代(10代、20代、30代…)に分類して集計するのが定番です。

MySQL での年代別集計

MySQL – 年代別の人数集計
SELECT
    CONCAT(
        FLOOR(TIMESTAMPDIFF(YEAR, birthday, CURDATE()) / 10) * 10,
        '代'
    ) AS age_group,
    COUNT(*) AS count
FROM
    users
GROUP BY
    FLOOR(TIMESTAMPDIFF(YEAR, birthday, CURDATE()) / 10) * 10
ORDER BY
    age_group;

実行結果

age_group | count
----------+------
10代      |     1
20代      |     2
30代      |     1
40代      |     2

CASE式で自由にグループを定義する

10歳刻みではなく、ビジネス要件に合わせたカスタムグループを作ることもできます。

MySQL – カスタム年齢グループ
SELECT
    CASE
        WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) < 18
            THEN '未成年'
        WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 18 AND 39
            THEN '若年層'
        WHEN TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 40 AND 64
            THEN '中年層'
        ELSE 'シニア'
    END AS age_category,
    COUNT(*) AS count
FROM
    users
GROUP BY
    age_category
ORDER BY
    MIN(TIMESTAMPDIFF(YEAR, birthday, CURDATE()));

実行結果

age_category | count
-------------+------
未成年       |     1
若年層       |     3
中年層       |     2

PostgreSQL での年代別集計

PostgreSQL – 年代別集計
SELECT
    (EXTRACT(YEAR FROM AGE(birthday))::int / 10) * 10
        || '代' AS age_group,
    COUNT(*) AS count
FROM
    users
GROUP BY
    age_group
ORDER BY
    age_group;

WHERE句で年齢フィルタリング

特定の年齢条件でデータを絞り込むケースも多いです。成人チェックシニア割引対象ターゲット層の抽出など、実務でよく使うパターンを紹介します。

成人(18歳以上)のユーザーを取得(MySQL)

MySQL – 成人のみ抽出
SELECT
    name,
    birthday,
    TIMESTAMPDIFF(YEAR, birthday, CURDATE()) AS age
FROM
    users
WHERE
    TIMESTAMPDIFF(YEAR, birthday, CURDATE()) >= 18;

年齢範囲で絞り込み(BETWEEN)

MySQL – 20代のユーザー
-- 20歳以上30歳未満(20代)のユーザーを取得
SELECT
    name,
    birthday,
    TIMESTAMPDIFF(YEAR, birthday, CURDATE()) AS age
FROM
    users
WHERE
    TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN 20 AND 29;

実行結果

name       | birthday   | age
-----------+------------+----
鈴木花子   | 2000-02-29 |  26
高橋美咲   | 2005-01-01 |  21

パフォーマンスを考慮した書き方

WHERE句で関数を使うとインデックスが効かない場合があります。大量データでパフォーマンスが問題になる場合は、誕生日の範囲に変換してインデックスを活用しましょう。

MySQL – インデックス活用パターン
-- 20歳以上30歳未満を「誕生日の範囲」に変換
SELECT
    name,
    birthday,
    TIMESTAMPDIFF(YEAR, birthday, CURDATE()) AS age
FROM
    users
WHERE
    birthday BETWEEN
        DATE_SUB(CURDATE(), INTERVAL 30 YEAR)
        AND
        DATE_SUB(CURDATE(), INTERVAL 20 YEAR);

ポイント:birthday 列に直接比較することでINDEXが効くようになり、大量レコード(数十万件以上)でもパフォーマンスが維持されます。WHERE句の関数適用は対象列が左辺にある場合にインデックスを無効化するため、可能な限りこの「逆算パターン」を使いましょう。

実務で使える年齢計算パターン

パターン1: 今月誕生日のユーザーを取得

MySQL – 今月の誕生日ユーザー
SELECT
    name,
    birthday,
    TIMESTAMPDIFF(YEAR, birthday, CURDATE()) + 1 AS next_age
FROM
    users
WHERE
    MONTH(birthday) = MONTH(CURDATE());

パターン2: 年齢付きのユーザー一覧VIEW

年齢を毎回計算するのは手間なので、VIEWを作成しておくと便利です。

MySQL – VIEW作成
CREATE VIEW v_users_with_age AS
SELECT
    id,
    name,
    birthday,
    TIMESTAMPDIFF(YEAR, birthday, CURDATE()) AS age,
    CONCAT(
        FLOOR(TIMESTAMPDIFF(YEAR, birthday, CURDATE()) / 10) * 10,
        '代'
    ) AS age_group
FROM
    users;

-- VIEW を使って簡単にクエリ
SELECT * FROM v_users_with_age WHERE age >= 20;

パターン3: 年齢の統計情報を取得

MySQL – 年齢の統計情報
SELECT
    COUNT(*) AS total_users,
    AVG(TIMESTAMPDIFF(YEAR, birthday, CURDATE())) AS avg_age,
    MIN(TIMESTAMPDIFF(YEAR, birthday, CURDATE())) AS min_age,
    MAX(TIMESTAMPDIFF(YEAR, birthday, CURDATE())) AS max_age
FROM
    users;

実行結果

total_users | avg_age | min_age | max_age
------------+---------+---------+--------
          6 |  30.83  |      15 |      48

パターン4: 年齢計算を使ったJOIN

MySQL – 注文データと年齢を結合
-- 年代別の売上集計
SELECT
    CONCAT(
        FLOOR(TIMESTAMPDIFF(YEAR, u.birthday, CURDATE()) / 10) * 10,
        '代'
    ) AS age_group,
    COUNT(*) AS order_count,
    SUM(o.amount) AS total_amount
FROM
    users u
    INNER JOIN orders o ON u.id = o.user_id
GROUP BY
    age_group
ORDER BY
    age_group;

よくあるエラーと対処法

問題 原因 対処法
年齢が1歳多い SQL ServerのDATEDIFFが年の差だけを返す CASE式で誕生日前後の補正を追加
NULLが返される birthday列がNULLのレコードがある COALESCEでデフォルト値を設定、またはWHERE句でNULLを除外
負の値が返される 引数の順序が逆(終了日と開始日) TIMESTAMPDIFF(YEAR, birthday, CURDATE()) の順序を確認
文字列型エラー birthday列がVARCHAR型で格納されている CAST(birthday AS DATE)で型変換してから計算
パフォーマンス低下 WHERE句で関数を使いインデックスが効かない DATE_SUBで誕生日の範囲に変換(逆算パターン)

NULL対策の実装例

MySQL – NULL安全な年齢計算
-- birthday が NULL の場合も安全に処理
SELECT
    name,
    birthday,
    CASE
        WHEN birthday IS NULL THEN '未登録'
        ELSE CAST(
            TIMESTAMPDIFF(YEAR, birthday, CURDATE())
            AS CHAR
        )
    END AS age_display
FROM
    users;

文字列型の誕生日を変換する

MySQL – VARCHAR型の誕生日を変換
-- birthday が '19900515' のような文字列の場合
SELECT
    name,
    TIMESTAMPDIFF(
        YEAR,
        STR_TO_DATE(birthday, '%Y%m%d'),
        CURDATE()
    ) AS age
FROM
    users;

各RDBMSの日付関連関数 まとめ

年齢計算で使う日付関数はRDBMSごとに異なります。よく使う関数の対応表をまとめます。

用途 MySQL PostgreSQL SQL Server Oracle
現在日付 CURDATE() CURRENT_DATE GETDATE() SYSDATE
年の差 TIMESTAMPDIFF AGE() + EXTRACT DATEDIFF MONTHS_BETWEEN
月の取得 MONTH() EXTRACT(MONTH FROM ...) MONTH() EXTRACT(MONTH FROM ...)
日の取得 DAY() EXTRACT(DAY FROM ...) DAY() EXTRACT(DAY FROM ...)
日付の減算 DATE_SUB() date - INTERVAL DATEADD() ADD_MONTHS()

まとめ

SQLで誕生日から年齢を計算する方法を、主要4つのRDBMS別に解説しました。最後にポイントを整理します。

この記事のまとめ

  • MySQLTIMESTAMPDIFF(YEAR, birthday, CURDATE()) が最もシンプルで正確
  • PostgreSQLEXTRACT(YEAR FROM AGE(birthday)) で専用関数を活用
  • SQL ServerDATEDIFF だけではNG。CASE式で誕生日補正が必須
  • OracleTRUNC(MONTHS_BETWEEN(SYSDATE, birthday) / 12) で正確に算出
  • うるう年(2/29)生まれの扱いに注意。各RDBMSの挙動を検証しておくと安心
  • 年代別集計FLOOR(年齢 / 10) * 10 で10歳刻みに変換
  • パフォーマンスを考慮するなら、WHERE句では DATE_SUB で誕生日範囲に変換
  • NULL対策を忘れずに。CASECOALESCE で安全に処理

年齢計算は一見シンプルに見えますが、RDBMS間の違いうるう年の境界パフォーマンスなど、実務では多くの考慮点があります。この記事で紹介したパターンを活用して、正確で効率的な年齢計算を実装してください。