「SQLで、現在の行の次の行のデータを取得したい…」
データベースを扱っていると、前後のレコード(行)の値を参照したくなることがありますよね。例えば、売上データで前月との比較をしたい、ログデータで前後のイベントを関連付けたい、といった場面です。
通常のSELECT文では、1行ずつ独立してデータを取得しますが、実は次のレコードや前のレコードの値を同時に取得する方法があるんです。
この記事では、SQLで次のレコードの値を取得するさまざまな方法を、初心者にも分かりやすく解説します。LEAD関数を使った最新の方法から、古いデータベースでも使える自己結合まで、あなたの環境に合った方法が必ず見つかるはずです。
次のレコードの値とは?基本概念を理解しよう

まず、「次のレコード」という概念について説明しましょう。
レコードの順序
データベースのテーブルには、基本的に決まった順序がありません。ただし、SELECT文でORDER BYを使えば、特定の列(カラム)の値に基づいて並べ替えられます。
この並び替えられた状態で、ある行の「次の行」のデータを取得するのが今回のテーマです。
具体例で理解する
例えば、以下のような売上テーブルがあるとします。
| id | 日付 | 売上金額 |
|---|---|---|
| 1 | 2024-01-01 | 10000 |
| 2 | 2024-01-02 | 15000 |
| 3 | 2024-01-03 | 12000 |
| 4 | 2024-01-04 | 18000 |
日付順に並べたとき、2024-01-01の「次の日」である2024-01-02の売上金額(15000)を、同じ行に表示したい場合があります。
こうすることで、前日比を簡単に計算できるようになるんです。
なぜ次のレコードの値が必要?
次のレコードの値を取得できると、以下のような分析が可能になります。
- 前後の比較(前日比、前月比など)
- 増減の計算
- 連続したイベントの関連付け
- ランキングの差分計算
- トレンド分析
データ分析では非常によく使われるテクニックですね。
【最新・推奨】LEAD関数で次のレコードを取得する
最も一般的で効率的な方法は、LEAD関数を使うことです。
LEAD関数とは?
LEAD関数は、ウィンドウ関数の一種で、現在の行から指定した数だけ後ろの行の値を取得できます。
SQL:2003標準で導入されたため、比較的新しいデータベースシステムなら使用できますよ。
基本的な構文
LEAD(列名, オフセット, デフォルト値) OVER (ORDER BY 並び替え列)
パラメータの説明
- 列名:取得したい列の名前
- オフセット:何行後ろを取得するか(省略時は1)
- デフォルト値:次の行がない場合に返す値(省略時はNULL)
- ORDER BY:どの順序で「次」を判断するか
基本的な使用例
次の日の売上金額を取得する例です。
SELECT
日付,
売上金額,
LEAD(売上金額) OVER (ORDER BY 日付) AS 次の日の売上
FROM
売上テーブル
ORDER BY
日付;
実行結果
| 日付 | 売上金額 | 次の日の売上 |
|---|---|---|
| 2024-01-01 | 10000 | 15000 |
| 2024-01-02 | 15000 | 12000 |
| 2024-01-03 | 12000 | 18000 |
| 2024-01-04 | 18000 | NULL |
最後の行は「次の日」がないので、NULLが返されます。
オフセットを指定する
2行後の値を取得したい場合は、オフセットに2を指定します。
SELECT
日付,
売上金額,
LEAD(売上金額, 2) OVER (ORDER BY 日付) AS 2日後の売上
FROM
売上テーブル
ORDER BY
日付;
デフォルト値を指定する
次の行がない場合に、NULLではなく特定の値を返したいときは、デフォルト値を指定します。
SELECT
日付,
売上金額,
LEAD(売上金額, 1, 0) OVER (ORDER BY 日付) AS 次の日の売上
FROM
売上テーブル
ORDER BY
日付;
これで、最後の行の「次の日の売上」は0になります。
PARTITION BYで グループごとに処理
複数のカテゴリがある場合、PARTITION BYを使うと、カテゴリごとに次のレコードを取得できます。
SELECT
カテゴリ,
日付,
売上金額,
LEAD(売上金額) OVER (PARTITION BY カテゴリ ORDER BY 日付) AS 次の日の売上
FROM
売上テーブル
ORDER BY
カテゴリ, 日付;
これで、商品Aなら商品Aの中で、商品Bなら商品Bの中で、次のレコードが取得されます。
LAG関数で前のレコードを取得する
次のレコードではなく、前のレコードの値が必要な場合は、LAG関数を使います。
LAG関数の基本構文
LAG(列名, オフセット, デフォルト値) OVER (ORDER BY 並び替え列)
使い方はLEAD関数とほぼ同じですが、取得する方向が逆になります。
使用例
前の日の売上金額を取得する例です。
SELECT
日付,
売上金額,
LAG(売上金額) OVER (ORDER BY 日付) AS 前の日の売上
FROM
売上テーブル
ORDER BY
日付;
実行結果
| 日付 | 売上金額 | 前の日の売上 |
|---|---|---|
| 2024-01-01 | 10000 | NULL |
| 2024-01-02 | 15000 | 10000 |
| 2024-01-03 | 12000 | 15000 |
| 2024-01-04 | 18000 | 12000 |
最初の行は「前の日」がないので、NULLになりますね。
前日比を計算する例
LAG関数を使えば、前日との差額や増減率を簡単に計算できます。
SELECT
日付,
売上金額,
売上金額 - LAG(売上金額) OVER (ORDER BY 日付) AS 前日比,
ROUND((売上金額 - LAG(売上金額) OVER (ORDER BY 日付)) * 100.0 / LAG(売上金額) OVER (ORDER BY 日付), 2) AS 前日比率
FROM
売上テーブル
ORDER BY
日付;
これで、前日からの増減額と増減率が一度に計算できます。
【古いDB対応】自己結合で次のレコードを取得する
LEAD関数が使えない古いデータベースシステムでは、自己結合という方法を使います。
自己結合とは?
自己結合とは、同じテーブルを2つの別名で扱い、それらを結合する方法です。1つのテーブルを「現在の行」として、もう1つを「次の行」として扱うんです。
基本的な構文
SELECT
t1.日付,
t1.売上金額,
t2.売上金額 AS 次の日の売上
FROM
売上テーブル AS t1
LEFT JOIN
売上テーブル AS t2
ON t2.日付 = (
SELECT MIN(日付)
FROM 売上テーブル
WHERE 日付 > t1.日付
)
ORDER BY
t1.日付;
この方法では、サブクエリで「t1の日付より大きい日付の中で最小のもの」を探して、それと結合しています。
連番IDがある場合の簡単な方法
テーブルに連番のIDがある場合は、もっとシンプルに書けます。
SELECT
t1.id,
t1.日付,
t1.売上金額,
t2.売上金額 AS 次の日の売上
FROM
売上テーブル AS t1
LEFT JOIN
売上テーブル AS t2
ON t2.id = t1.id + 1
ORDER BY
t1.id;
ただし、この方法は、IDが連続していて欠番がない場合にのみ有効です。
自己結合の注意点
自己結合は、LEAD関数に比べて処理が遅くなることがあります。特に、大量のデータを扱う場合は、インデックスを適切に設定することが重要ですよ。
サブクエリを使う方法
サブクエリを使って、次のレコードを取得する方法もあります。
相関サブクエリを使う
SELECT
日付,
売上金額,
(SELECT 売上金額
FROM 売上テーブル AS t2
WHERE t2.日付 > t1.日付
ORDER BY t2.日付
LIMIT 1) AS 次の日の売上
FROM
売上テーブル AS t1
ORDER BY
日付;
この方法では、各行ごとにサブクエリが実行されるため、データ量が多いと処理が遅くなる可能性があります。
MySQL 8.0未満での対応
MySQL 8.0未満では、LEAD関数が使えません。その場合は、この相関サブクエリの方法が有効です。
MySQL用の書き方
SELECT
日付,
売上金額,
(SELECT 売上金額
FROM 売上テーブル
WHERE 日付 > t1.日付
ORDER BY 日付 ASC
LIMIT 1) AS 次の日の売上
FROM
売上テーブル AS t1
ORDER BY
日付;
実践的な使用例

実際の業務でよく使われるパターンを紹介します。
例1:売上の増減を可視化
SELECT
日付,
売上金額,
LAG(売上金額) OVER (ORDER BY 日付) AS 前日売上,
売上金額 - LAG(売上金額) OVER (ORDER BY 日付) AS 増減額,
CASE
WHEN 売上金額 > LAG(売上金額) OVER (ORDER BY 日付) THEN '増加'
WHEN 売上金額 < LAG(売上金額) OVER (ORDER BY 日付) THEN '減少'
ELSE '変化なし'
END AS トレンド
FROM
売上テーブル
ORDER BY
日付;
これで、売上が増加しているか減少しているかが一目で分かります。
例2:連続した欠席日数を計算
出席記録から、連続した欠席を検出する例です。
SELECT
生徒ID,
日付,
出席状況,
LEAD(日付) OVER (PARTITION BY 生徒ID ORDER BY 日付) AS 次の日付,
DATEDIFF(
LEAD(日付) OVER (PARTITION BY 生徒ID ORDER BY 日付),
日付
) - 1 AS 間隔日数
FROM
出席記録テーブル
WHERE
出席状況 = '欠席'
ORDER BY
生徒ID, 日付;
例3:株価の変動を分析
SELECT
取引日,
終値,
LAG(終値, 1) OVER (ORDER BY 取引日) AS 前日終値,
終値 - LAG(終値, 1) OVER (ORDER BY 取引日) AS 値幅,
ROUND((終値 - LAG(終値, 1) OVER (ORDER BY 取引日)) * 100.0 / LAG(終値, 1) OVER (ORDER BY 取引日), 2) AS 騰落率
FROM
株価テーブル
WHERE
銘柄コード = 'AAPL'
ORDER BY
取引日 DESC
LIMIT 30;
例4:ページ遷移の分析
ユーザーのページ閲覧履歴から、次に見たページを取得する例です。
SELECT
ユーザーID,
閲覧時刻,
ページURL,
LEAD(ページURL) OVER (PARTITION BY ユーザーID ORDER BY 閲覧時刻) AS 次のページ,
TIMESTAMPDIFF(
SECOND,
閲覧時刻,
LEAD(閲覧時刻) OVER (PARTITION BY ユーザーID ORDER BY 閲覧時刻)
) AS 滞在秒数
FROM
アクセスログテーブル
WHERE
ユーザーID = 12345
ORDER BY
閲覧時刻;
これで、各ページの滞在時間も計算できますね。
例5:在庫の補充タイミングを検出
SELECT
日付,
在庫数,
LEAD(在庫数) OVER (ORDER BY 日付) AS 翌日在庫数,
CASE
WHEN LEAD(在庫数) OVER (ORDER BY 日付) < 100
AND 在庫数 >= 100 THEN '補充必要'
ELSE 'OK'
END AS 補充判定
FROM
在庫テーブル
WHERE
商品ID = 'PROD001'
ORDER BY
日付;
データベースごとの違い
主要なデータベースシステムでのLEAD/LAG関数の対応状況を確認しておきましょう。
PostgreSQL
PostgreSQL 8.4以降で、LEAD/LAG関数が使えます。
SELECT
id,
value,
LEAD(value) OVER (ORDER BY id) AS next_value
FROM
test_table;
PostgreSQLは、ウィンドウ関数のサポートが充実していますよ。
MySQL
MySQL 8.0以降で、LEAD/LAG関数が使えます。
MySQL 5.7以前では使えないので、自己結合やサブクエリを使う必要があります。
-- MySQL 8.0以降
SELECT
id,
value,
LEAD(value) OVER (ORDER BY id) AS next_value
FROM
test_table;
SQL Server
SQL Server 2012以降で、LEAD/LAG関数が使えます。
SELECT
id,
value,
LEAD(value) OVER (ORDER BY id) AS next_value
FROM
test_table;
Oracle
Oracle Database 11g以降で、LEAD/LAG関数が使えます。
SELECT
id,
value,
LEAD(value) OVER (ORDER BY id) AS next_value
FROM
test_table;
SQLite
SQLite 3.25.0以降で、LEAD/LAG関数が使えます。
それ以前のバージョンでは、自己結合を使う必要があります。
パフォーマンスの考慮
次のレコードを取得する際のパフォーマンスについて理解しておきましょう。
ウィンドウ関数のパフォーマンス
LEAD/LAG関数などのウィンドウ関数は、一般的に効率的に動作します。データベースエンジンが最適化してくれるからです。
ただし、PARTITION BYで多数のグループに分割したり、ORDER BYで複雑な並び替えをしたりすると、処理が遅くなることがあります。
インデックスの重要性
ORDER BY句で指定する列には、インデックスを作成しておくことをおすすめします。
CREATE INDEX idx_sales_date ON 売上テーブル(日付);
これで、並び替え処理が高速化されますよ。
自己結合のパフォーマンス
自己結合は、テーブルのサイズが大きくなると、処理時間が大幅に増加します。
可能な限り、LEAD/LAG関数を使う方が効率的です。
大量データの処理
数百万行以上のデータを扱う場合は、以下の工夫が有効です。
- WHERE句で対象を絞り込む
- 必要な列だけを取得する
- 適切なインデックスを設定する
- パーティショニングを検討する
よくある質問と回答
複数の次のレコードを取得できる?
はい、LEAD関数を複数回使えば可能です。
SELECT
日付,
売上金額,
LEAD(売上金額, 1) OVER (ORDER BY 日付) AS 1日後,
LEAD(売上金額, 2) OVER (ORDER BY 日付) AS 2日後,
LEAD(売上金額, 3) OVER (ORDER BY 日付) AS 3日後
FROM
売上テーブル;
NULLの扱いはどうなる?
LEAD/LAG関数は、NULL値も通常の値として扱います。NULL値をスキップして次の値を取得することはできません。
NULL値を除外したい場合は、WHERE句で事前にフィルタリングする必要があります。
日付が連続していない場合はどうする?
日付が欠けている場合でも、LEAD関数は「次に存在する行」を取得します。
-- 2024-01-02がなくても、2024-01-01の次は2024-01-03になる
SELECT
日付,
LEAD(日付) OVER (ORDER BY 日付) AS 次の日付
FROM
売上テーブル;
文字列でも使える?
はい、LEAD/LAG関数は数値だけでなく、文字列や日付など、あらゆるデータ型で使えます。
SELECT
id,
商品名,
LEAD(商品名) OVER (ORDER BY id) AS 次の商品
FROM
商品テーブル;
まとめ:状況に応じて最適な方法を選ぼう
SQLで次のレコードの値を取得する方法について、詳しく解説しました。
重要なポイントをおさらい
- 最新のDBなら、LEAD関数が最も簡単で効率的
- 前のレコードが必要ならLAG関数を使う
- 古いDBでは自己結合やサブクエリで対応
- PARTITION BYでグループごとに処理できる
- ORDER BYで並び順を制御
- インデックスを適切に設定してパフォーマンスを改善
次のレコードの値を取得できるようになると、データ分析の幅が大きく広がります。前日比、前月比、トレンド分析など、さまざまな場面で活用できるでしょう。
この記事を参考に、あなたのデータ分析がより効率的になることを願っています!


コメント