【SQL】前年のデータを取得する方法|RDBMS別構文・前年同月比・LAG関数まで実務パターン解説

【SQL】前年のデータを取得する方法|RDBMS別構文・前年同月比・LAG関数まで実務パターン解説 SQL

売上の前年比較、去年の同じ月のデータ抽出、年次レポート作成――SQLで前年のデータを取得する処理は実務で頻繁に登場します。「昨年の売上と今年を比べたい」「前年同月比を出したい」という場面も多いですが、RDBMS(データベース)ごとに日付関数の書き方が異なるため、環境が変わると戸惑うことも多いのではないでしょうか。

この記事では、MySQL・PostgreSQL・SQL Server・Oracleの4つのRDBMSに対応した前年データ取得の構文を体系的に整理し、さらに前年同月比LAG関数を使った実務パターンまで解説します。

この記事で分かること

  • 前年のデータを取得する基本構文(MySQL・PostgreSQL・SQL Server・Oracle)
  • YEAR関数DATE_SUBEXTRACTの使い分け
  • 前年同月・前年同期など実務でよく使うパターン
  • LAG関数を使った前年比(前年同月比)の算出
  • 条件付き集計(CASE式)で今年と前年を横並びにする方法
  • うるう年・年度ずれなどの注意点とエッジケース
スポンサーリンク

サンプルテーブル

本記事では、以下の売上テーブルを使ってSQL文を解説します。

id product_name sale_date amount
1商品A2024-03-1515000
2商品B2024-06-2023000
3商品A2024-11-1018000
4商品A2025-03-1517000
5商品B2025-06-1825000
6商品C2025-09-0112000

【SQL基本】前年1年分のデータを取得する

最もよく使うパターンは「前年のすべてのデータ」を取得する方法です。RDBMS別に構文を紹介します。

方法1:YEAR関数で年を比較する(MySQL)

MySQL — YEAR関数で前年のデータを取得

SELECT *
FROM sales
WHERE YEAR(sale_date) = YEAR(CURDATE()) - 1;

YEAR()関数は日付から年だけを整数で取り出します。CURDATE()は今日の日付を返すので、YEAR(CURDATE()) - 1で前年の年(例:2024)になります。

▼ 実行結果(2025年に実行した場合)

idproduct_namesale_dateamount
1商品A2024-03-1515000
2商品B2024-06-2023000
3商品A2024-11-1018000

方法2:EXTRACT関数を使う(PostgreSQL / MySQL / Oracle)

PostgreSQL / MySQL / Oracle — EXTRACT関数

SELECT *
FROM sales
WHERE EXTRACT(YEAR FROM sale_date) = EXTRACT(YEAR FROM CURRENT_DATE) - 1;

EXTRACT(YEAR FROM ...)はSQL標準の構文で、PostgreSQL・MySQL・Oracleで使えます。可搬性の高い書き方です。

方法3:DATEPART関数を使う(SQL Server)

SQL Server — DATEPART / YEAR関数

-- 方法A:YEAR関数
SELECT *
FROM sales
WHERE YEAR(sale_date) = YEAR(GETDATE()) - 1;

-- 方法B:DATEPART関数
SELECT *
FROM sales
WHERE DATEPART(YEAR, sale_date) = DATEPART(YEAR, GETDATE()) - 1;

RDBMS別 構文比較表

RDBMS 年の抽出 現在日付
MySQLYEAR(col) / EXTRACT(YEAR FROM col)CURDATE() / NOW()
PostgreSQLEXTRACT(YEAR FROM col) / DATE_PART('year', col)CURRENT_DATE
SQL ServerYEAR(col) / DATEPART(YEAR, col)GETDATE()
OracleEXTRACT(YEAR FROM col) / TO_CHAR(col, 'YYYY')SYSDATE

SQLで前年の「特定期間」を取得する

「前年1年分」ではなく、前年の特定の月や期間を取得したい場合は、日付の範囲指定と組み合わせます。

前年の同じ月のデータ(前年同月)

MySQL — 前年同月のデータ

SELECT *
FROM sales
WHERE YEAR(sale_date) = YEAR(CURDATE()) - 1
  AND MONTH(sale_date) = MONTH(CURDATE());

PostgreSQL — 前年同月のデータ

SELECT *
FROM sales
WHERE EXTRACT(YEAR FROM sale_date) = EXTRACT(YEAR FROM CURRENT_DATE) - 1
  AND EXTRACT(MONTH FROM sale_date) = EXTRACT(MONTH FROM CURRENT_DATE);

前年の今日から1年前まで(過去1年間)

「前年」ではなく「今日からちょうど1年前までのデータ」を取得したい場合は、DATE_SUB / DATEADD / INTERVALを使います。

RDBMS別 — 過去1年間のデータ取得

-- MySQL
SELECT * FROM sales
WHERE sale_date >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR);

-- PostgreSQL
SELECT * FROM sales
WHERE sale_date >= CURRENT_DATE - INTERVAL '1 year';

-- SQL Server
SELECT * FROM sales
WHERE sale_date >= DATEADD(YEAR, -1, GETDATE());

-- Oracle
SELECT * FROM sales
WHERE sale_date >= ADD_MONTHS(SYSDATE, -12);

「前年」と「過去1年間」の違い

YEAR(sale_date) = YEAR(CURDATE()) - 1前年(2024年1月1日〜12月31日)のデータを取得します。
sale_date >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR)今日から1年前〜今日までのデータを取得します。
レポートの要件に応じて使い分けてください。日付の範囲指定について詳しくは「日付の範囲指定を行う方法」で解説しています。

前年の特定日付範囲(例:前年4月〜9月)

MySQL — 前年の上半期(4月〜9月)のデータ

SELECT *
FROM sales
WHERE sale_date BETWEEN
    CONCAT(YEAR(CURDATE()) - 1, '-04-01')
    AND
    CONCAT(YEAR(CURDATE()) - 1, '-09-30');

SQLで前年同月比を算出する(CASE式による条件付き集計)

売上レポートでは、今年と前年の売上を横並びで比較したいことがよくあります。CASE式とSUM関数を組み合わせると、1回のクエリで実現できます。

MySQL — 月別の今年・前年売上を横並びで表示

SELECT
    MONTH(sale_date) AS sale_month,
    SUM(CASE WHEN YEAR(sale_date) = YEAR(CURDATE())
         THEN amount ELSE 0 END) AS this_year,
    SUM(CASE WHEN YEAR(sale_date) = YEAR(CURDATE()) - 1
         THEN amount ELSE 0 END) AS last_year,
    -- 前年同月比(%)
    ROUND(
        SUM(CASE WHEN YEAR(sale_date) = YEAR(CURDATE()) THEN amount ELSE 0 END)
        / NULLIF(SUM(CASE WHEN YEAR(sale_date) = YEAR(CURDATE()) - 1 THEN amount ELSE 0 END), 0)
        * 100, 1
    ) AS yoy_ratio
FROM sales
WHERE YEAR(sale_date) IN (YEAR(CURDATE()), YEAR(CURDATE()) - 1)
GROUP BY MONTH(sale_date)
ORDER BY sale_month;

▼ 実行結果(サンプルデータで実行した場合)

sale_monththis_yearlast_yearyoy_ratio
31700015000113.3
62500023000108.7
9120000NULL
110180000.0

NULLIF でゼロ除算を防ぐ

前年のデータが存在しない月では、前年売上が0になりゼロ除算エラーが発生します。NULLIF(値, 0)を使うと、値が0の場合にNULLを返し、結果もNULLになるためエラーを回避できます。

SQL LAG関数で前年比を算出する(ウィンドウ関数)

月次データが連続している場合、LAG関数(ウィンドウ関数)を使うとシンプルに前年比を算出できます。LAG関数はN行前のデータを参照できるため、月次集計なら12行前 = 前年同月のデータを取得できます。

MySQL 8.0+ — LAG関数で前年同月比(※PostgreSQL/SQL Server/OracleはEXTRACTやDATEPARTに置き換え)

WITH monthly_sales AS (
    SELECT
        YEAR(sale_date)  AS sale_year,
        MONTH(sale_date) AS sale_month,
        SUM(amount)       AS total_amount
    FROM sales
    GROUP BY YEAR(sale_date), MONTH(sale_date)
)
SELECT
    sale_year,
    sale_month,
    total_amount,
    -- 12行前(前年同月)の売上を取得
    LAG(total_amount, 12) OVER (
        ORDER BY sale_year, sale_month
    ) AS prev_year_amount,
    -- 前年同月比(%)
    ROUND(
        total_amount * 100.0
        / NULLIF(LAG(total_amount, 12) OVER (
            ORDER BY sale_year, sale_month
        ), 0), 1
    ) AS yoy_ratio
FROM monthly_sales
ORDER BY sale_year, sale_month;

LAG関数を使う際の注意点

LAG関数は行の物理的な位置で参照するため、月のデータが欠けていると正確な前年同月にならない場合があります。データに歯抜けがある場合は、CASE式による条件付き集計のほうが確実です。
WITH句の使い方は「WITH句を使って副問合せを再利用する方法」で詳しく解説しています。

応用:商品別の前年同月比(PARTITION BY)

実務では、全体の前年比だけでなく商品別・カテゴリ別の前年同月比を算出するケースが非常に多いです。PARTITION BYを加えるだけで実現できます。

MySQL 8.0+ — 商品別の前年同月比をLAG関数で算出

WITH product_monthly AS (
    SELECT
        product_name,
        YEAR(sale_date)  AS sale_year,
        MONTH(sale_date) AS sale_month,
        SUM(amount)       AS total_amount
    FROM sales
    GROUP BY product_name, YEAR(sale_date), MONTH(sale_date)
)
SELECT
    product_name,
    sale_year,
    sale_month,
    total_amount,
    LAG(total_amount, 12) OVER (
        PARTITION BY product_name
        ORDER BY sale_year, sale_month
    ) AS prev_year_amount
FROM product_monthly
ORDER BY product_name, sale_year, sale_month;

PARTITION BY product_nameを指定することで、商品ごとに独立してLAG関数が適用されます。カテゴリ別ならPARTITION BY categoryに変えるだけです。

SQL自己結合(SELF JOIN)で前年データと比較する

テーブルを自分自身と結合し、今年と前年のデータを行単位で対応づける方法もあります。

MySQL — 自己結合で今年と前年の月別売上を比較

SELECT
    cur.sale_month,
    cur.total_amount  AS this_year,
    prev.total_amount AS last_year,
    ROUND(cur.total_amount / NULLIF(prev.total_amount, 0) * 100, 1) AS yoy_ratio
FROM (
    -- 今年の月別集計
    SELECT MONTH(sale_date) AS sale_month,
           SUM(amount)       AS total_amount
    FROM sales
    WHERE YEAR(sale_date) = YEAR(CURDATE())
    GROUP BY MONTH(sale_date)
) cur
LEFT JOIN (
    -- 前年の月別集計
    SELECT MONTH(sale_date) AS sale_month,
           SUM(amount)       AS total_amount
    FROM sales
    WHERE YEAR(sale_date) = YEAR(CURDATE()) - 1
    GROUP BY MONTH(sale_date)
) prev ON cur.sale_month = prev.sale_month
ORDER BY cur.sale_month;

LEFT JOIN を使うことで、前年にデータがない月も結果に含まれます(last_yearがNULLになる)。INNER JOINにすると前年と今年の両方にデータがある月だけが返ります。テーブルの結合について詳しくは「INNER JOIN、LEFT JOIN、RIGHT JOINの使い方」を参照してください。

SQL前年データ取得 方法の選び方ガイド

やりたいこと おすすめの方法 特徴
前年のデータを抽出するだけYEAR関数 / EXTRACTシンプルで分かりやすい
過去1年間のデータを抽出DATE_SUB / DATEADD / INTERVAL動的な期間指定に強い
今年と前年を横並びで比較CASE式 + SUMテーブルスキャン1回で高速
月次データの前年同月比LAG関数(ウィンドウ関数)連続データに最適・SQL簡潔
前年と今年を行単位で対応づけ自己結合(SELF JOIN)柔軟だがサブクエリが増える

SQL前年データ取得の注意点とエッジケース

うるう年と月末日の扱い

DATE_SUB('2024-02-29', INTERVAL 1 YEAR)の結果は2023-02-28になります。2024年はうるう年ですが、2023年は違うため、自動的に月末日に調整されます。

うるう年のテスト

-- MySQL
SELECT DATE_SUB('2024-02-29', INTERVAL 1 YEAR);
-- 結果: 2023-02-28(自動で月末に調整される)

-- 逆パターン
SELECT DATE_SUB('2023-02-28', INTERVAL -1 YEAR);
-- 結果: 2024-02-28(2/29にはならない)

YEAR関数をWHERE句で使うとインデックスが効かない

WHERE YEAR(sale_date) = 2024のように日付列に関数を適用すると、インデックスが使われずテーブル全体をスキャンするため、大量データで遅くなります。

パフォーマンスを意識した書き方

-- ❌ インデックスが効かない
SELECT * FROM sales
WHERE YEAR(sale_date) = 2024;

-- ✅ インデックスが効く(範囲検索)
SELECT * FROM sales
WHERE sale_date >= '2024-01-01'
  AND sale_date <  '2025-01-01';

データ量が多い場合は、BETWEEN や不等号による範囲指定を使いましょう。日付の比較について詳しくは「日付の比較方法」で解説しています。

年度(4月始まり)の場合

会計年度が4月始まりの場合、単純な「前年」ではなく前年度のデータを取得する必要があります。

MySQL — 前年度のデータを取得(4月始まり)

-- 年度を算出するヘルパー式
-- 1月〜3月 → 前年が年度  4月〜12月 → 当年が年度
SELECT *
FROM sales
WHERE
    -- 年度を計算(月が3以下なら年-1)
    YEAR(sale_date) - (MONTH(sale_date) <= 3)
    =
    YEAR(CURDATE()) - (MONTH(CURDATE()) <= 3) - 1;

MONTH(sale_date) <= 3はMySQLでTRUE = 1FALSE = 0として扱われるため、1〜3月なら年から1を引いて年度を算出します。

時刻部分の影響

日付列がDATETIME型の場合、2024-12-31 23:59:59のようなデータはYEAR()で正しく2024年と判定されるため問題ありません。ただし、BETWEENで範囲指定する場合は時刻部分に注意が必要です。時間を無視して日付だけで比較する方法は「時間を無視して日付のみで比較する方法」を参照してください。

まとめ

前年データ取得のポイント

✅ 基本は YEAR(col) = YEAR(現在) - 1 で前年を取得
✅ 過去1年間は DATE_SUB / DATEADD / INTERVAL を使う
✅ 横並び比較は CASE式 + SUM(テーブルスキャン1回で高速)
✅ 連続月次データの前年同月比は LAG関数 が簡潔
✅ 大量データでは YEAR関数を避けて範囲指定(インデックス活用)

SQLで去年(昨年)のデータを取得する方法は、使うRDBMSや取得したいデータのパターンによって最適な方法が変わります。基本をマスターしたら、前年同月比やLAG関数を使った応用パターンにも挑戦してみてください。

関連記事として、前月のデータ取得は「前月のデータを取得する方法」、前日のデータ取得は「前日のデータを取得する方法」、BETWEEN演算子での範囲指定は「between演算子で抽出する範囲を指定する方法」も参考にしてください。