SQLのOFFSETとは?LIMITとの違い・ページングと性能対策完全解説
CONTENTS
「SQLで2ページ目を取得したい」
「LIMITとOFFSETの違いが曖昧…」
「ページングが不安定になって困っている」
「大きなOFFSETが遅いのはなぜ?」
このような悩みを持つエンジニアは非常に多いです。
OFFSETは“どこから取得するか”を決める機能、LIMITは“何件取得するか”を決める機能です。
しかし実務では、ORDER BYとの関係、性能問題、DB製品ごとの差異まで理解していないと事故につながります。
本記事では以下を網羅的に解説します。
- OFFSETとLIMITの基本構文
- ORDER BYとの本質的な関係
- 正しいページネーション実装
- よくある落とし穴
- パフォーマンス問題とキーセットページネーション
- DB製品ごとの書き方の違い
初心者にも分かりやすく、実務者が納得できる深さで解説します。
OFFSETとLIMITの基本概念
LIMITとは(取得件数を制御する)
LIMITは「返す最大件数」を指定します。
SELECT * FROM users LIMIT 10;
→ 最大10件返す
用途例:
- 最新1件だけ取得したい
- 一覧画面は10件ずつ表示したい
- デバッグ時に少数件だけ確認したい
LIMITはパフォーマンス改善の第一歩です。
不要な大量データ取得を防ぎ、メモリ・通信負荷を抑えます。
OFFSETとは(開始位置を制御する)
OFFSETは「先頭から何行スキップするか」を指定します。
SELECT * FROM users LIMIT 10 OFFSET 20;
→ 21行目から10件取得
OFFSETは0始まりです。
| OFFSET値 | 開始位置 |
|---|---|
| 0 | 1行目 |
| 1 | 2行目 |
| 10 | 11行目 |
LIMITとOFFSETの違い
| 機能 | 役割 |
|---|---|
| LIMIT | 取得件数を決める |
| OFFSET | 取得開始位置を決める |
この2つを組み合わせて初めて「ページネーション」が成立します。
なぜOFFSETはORDER BYとセットで使うべきなのか
ORDER BYがないと順序は保証されない
RDBMSでは、ORDER BYがなければ返却順は保証されません。
参考(公式ドキュメント)
PostgreSQL公式:https://www.postgresql.org/docs/current/queries-order.html
MySQL公式:https://dev.mysql.com/doc/refman/8.0/en/order-by-optimization.html
つまり、
SELECT * FROM users LIMIT 10 OFFSET 10;
は実行のたびに違う結果になる可能性があります。
安定したページングの条件
必ず以下を守る必要があります。
-
ORDER BYを指定する
-
同順位が発生しないよう一意性を担保する
例:
ORDER BY created_at DESC, id DESC
created_atだけでは同時刻が存在します。
主キーでタイブレークすることで安定します。
ページネーション実装の基本
ページ番号からOFFSETを計算する式
OFFSET = (page - 1) × per_page
例:1ページ10件
| page | OFFSET |
|---|---|
| 1 | 0 |
| 2 | 10 |
| 3 | 20 |
実装例(1ページ10件)
SELECT * FROM users ORDER BY id ASC LIMIT 10 OFFSET 20;
→ 3ページ目(21〜30件目)
API設計のベストプラクティス
推奨:
- page(1始まり)
- per_page(上限あり)
注意点:
- per_pageの最大値を制限(例:100)
- pageが異常値なら補正
- offsetを直接公開しない
よくある失敗パターン
1. ORDER BYなしでOFFSETを使う
→ ページ間で重複・欠落発生
2. 同順位で並びが不安定
ORDER BY created_at DESC
→ 同時刻データが多いとページ境界が揺れる
3. 更新が頻発するテーブル
ページ閲覧中にINSERTが入るとズレます。
例:
- 2ページ目閲覧中
- 先頭に新規データ追加
- 次ページ取得で重複発生
これはOFFSET方式の構造的弱点です。
なぜ大きなOFFSETは遅くなるのか?
本質的な理由
OFFSET 10000 の場合:
→ DBは最初の10000行を処理してから捨てる
つまり「読み飛ばしコスト」が発生します。
特にORDER BY付きの場合、ソートコストも加算されます。
参考:
PostgreSQL実行計画解説
https://www.postgresql.org/docs/current/using-explain.html
解決策:キーセット(キーベース)ページネーション
キーセット方式とは?
「何行目」ではなく
「どのキー以降」を指定する方式
SQL例
SELECT * FROM users WHERE id > :last_id ORDER BY id ASC LIMIT 10;
特徴:
- 深いページでも高速
- 重複・欠落が起きにくい
- 無限スクロール向き
複合ソートの場合
WHERE (created_at, id) < (:last_created_at, :last_id)
OFFSET方式 vs キーセット方式
| 項目 | OFFSET | キーセット |
|---|---|---|
| 任意ページジャンプ | ◎ | × |
| 深いページ性能 | △ | ◎ |
| 更新耐性 | △ | ◎ |
| 実装難易度 | 低 | 中 |
DB製品ごとの構文差
MySQL / PostgreSQL
LIMIT n OFFSET m
公式:
- MySQL:https://dev.mysql.com/doc/refman/8.0/en/select.html
- PostgreSQL:https://www.postgresql.org/docs/current/sql-select.html
SQL Server
ORDER BY id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
ORDER BY必須。
公式:https://learn.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql
Oracle
12c以降:
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
公式:https://docs.oracle.com/en/database/
SQLite
LIMIT 10 OFFSET 10
公式:https://sqlite.org/lang_select.html
実務で必ず確認すべき3点
-
任意ページジャンプは必要か?
-
ORDER BYは一意か?
-
深いページアクセスは発生するか?
これを決めずに実装すると後で作り直しになります。
まとめ
本記事の要点
- LIMITは件数制御
- OFFSETは開始位置制御
- 必ずORDER BYとセットで使う
- 同順位は主キーで確定させる
- 深いページは遅くなる
- 更新が多いならキーセット方式を検討
次にやるべきこと
- 自分のプロジェクトでORDER BYが安定しているか確認する
- インデックス設計を見直す
- 深いページアクセスがあるか分析する
- 必要ならキーセット方式に切り替える
エンジニアとして一段上に進むために
OFFSETを「なんとなく」使っている段階から、
性能・一貫性・UXまで考えられる設計ができる段階へ。
私たちはデータベース設計・パフォーマンス改善・API設計に強いエンジニアチームです。
- 実行計画を読める
- インデックス設計を語れる
- 負荷を見据えた設計ができる
そんな技術志向の仲間を探しています。
もしこの記事を読んで
「もっと深く設計を理解したい」
「パフォーマンス最適化を極めたい」
と感じた方は、ぜひ当社の採用情報もご覧ください。
高度な設計に本気で向き合える環境で、一緒にプロダクトを進化させていきましょう。
学んだSQLを、実務で使えるスキルにしたい方へ
本記事ではSQLの基本的な考え方や使い方を解説しましたが、
「実務で使えるレベルまで身につけたい」と感じた方も多いのではないでしょうか。
SQLは、文法を理解するだけでなく、
- どんなデータ構造で使われるのか
- どんなクエリが現場で求められるのか
を意識して学ぶことで、実践力が大きく変わります。
そうした「実務を見据えたSQL学習」を進めたい方には、
完全無料で学べるプログラミングスクール ZeroCode という選択肢もあります。
- SQLを含むWeb・データベース系スキルを体系的に学べる
- 未経験者でも実務を意識したカリキュラム
- 受講料・教材費がかからない完全無料の学習環境
- 完全オンラインでスキマ学習
※学習内容や進め方を確認するだけでもOKです