第06回: ORDER BYとLIMITで一覧最適化 — 「並び順が毎回違う」を今すぐ直す

章: 第2章: 実務クエリ基礎

ページング実装後に「件数が増えたら遅くなった」と気づいていませんか?

一覧画面でよくある失敗が「ORDER BYを書かずにLIMITだけ指定する」パターンです。ORDER BYなしのLIMITは、MySQLが内部でどの行を返すかを保証しないため、同じクエリでも実行するたびに結果の順番が変わるという不具合が起きます。さらに、データ量が増えると OFFSET が大きくなり、前ページ分を読み捨てるコストで急激に遅くなります。

表示順と件数制御を正しく設計すれば、一覧APIは安定してスケールします。

「なんとなくLIMIT」と「意図あるORDER BY + LIMIT」の差

問題は書き方ではなく、「ソート条件がインデックスと噛み合っているか」 の意識にあります。OFFSET方式の代替として、カーソルページング(最後に取得したIDを次のWHERE条件にする)も実務では広く使われます。

ORDER BY + LIMIT の書き方比較

パターン 問題点 推奨する書き方
LIMIT 20(ORDER BYなし) 取得順が保証されない ORDER BY created_at DESC LIMIT 20
OFFSET 10000 読み捨てコストで遅い カーソルページング(WHERE id < last_id)
ORDER BY name インデックスなしで全表ソート インデックスのあるカラムでソート
SELECT * + LIMIT 不要列も転送される 必要列のみ SELECT してから LIMIT

チェックポイント: OFFSET が大きくなるほど読み飛ばすコストが増えます。ページ数が多い一覧(管理画面のログ検索など)では、WHERE id < :last_id ORDER BY id DESC LIMIT 20 のようなカーソルページングへの切り替えを検討しましょう。

実際のコードのサンプル


SELECT id, title, created_at
FROM posts
ORDER BY created_at DESC
LIMIT 20 OFFSET 0;

まとめ & 次のステップ

  • ORDER BY なしの LIMIT は結果順が保証されないため、必ずセットで書く
  • ソートカラムにインデックスを張ると、ORDER BY + LIMIT の性能が大幅に改善する
  • OFFSET方式は大きなページ番号で遅くなるため、大規模データではカーソルページングを検討する
  • 必要列だけ SELECT することで転送データ量を絞り、全体のレスポンスを軽くする

次回は 「JOINの基本(INNER/LEFT)」 を学びます。複数テーブルの情報を1クエリで取得し、N+1問題を未然に防ぐ設計の考え方を確認します。

Related Articles