ORDER BYが重いときに見るべき実行計画とインデックスの直し方
まず押さえるORDER BYが重いときの見方
ORDER BYが重いときは、並び替えの指定だけでなく、読み取り件数、ソート処理、インデックス設計をまとめて確認することが大切です。
画面では20件だけ表示していても、内部では大量の行を読んでから並び替えている場合があります。
ORDER BYは最後の並び替えで重くなることがある
ORDER BYは結果を整えるための指定ですが、インデックスの並びを使えない場合はデータベース内部で追加のソートが発生します。
対象行が少なければ目立ちにくいですが、一覧画面や検索結果で大量データを扱うと、最後の並び替えが体感速度に影響します。
ソートが重いのか読み取りが多いのかを分けて考える
ORDER BYが遅いからといって、すぐORDER BY列にインデックスを作るのは早すぎる場合があります。
遅さの原因がソートではなく、WHERE句で十分に絞り込めず読み取り件数が多いことにある場合もあります。
実行計画で見るべきORDER BYのサイン
ORDER BYの改善では、SQLを直す前に実行計画で処理の流れを確認します。
実行計画を見ると、余計なソートがあるか、期待したインデックスが使われているか、返却件数に対して読み取り件数が多すぎないかを確認できます。
| 確認項目 | 見るポイント | 疑う原因 |
|---|---|---|
| ソート処理 | filesortやSORT ORDER BYが出ているか | インデックス順を使えていない |
| アクセス方式 | インデックススキャンか全表走査か | 条件や列順が合っていない |
| 読み取り件数 | 返却件数より大幅に多いか | 絞り込みが弱い |
| 利用インデックス | 期待したインデックスか | 複合インデックスの列順が合わない |
filesortやSORT ORDER BYが出ていないか
MySQLではfilesort、OracleではSORT ORDER BYのような表示がある場合、追加の並び替え処理が発生している可能性があります。
これらが必ず悪いわけではありませんが、処理時間が長いSQLでは見直し候補になります。
インデックススキャンか全表走査かを確認する
適切なインデックスを順番にたどれる場合、データベースは並び替え済みの状態で結果を取り出せることがあります。
一方で全表走査に近い動きになっている場合は、多くの行を読んだあとにソートしている可能性があります。
読み取り件数と返却件数の差を見る
LIMITで20件だけ返しているSQLでも、内部で読んでいる行数が20件に近いとは限りません。
読み取り件数と返却件数の差が大きい場合は、ORDER BYだけでなくWHERE句やインデックス全体を見直します。
ORDER BYでインデックスが効く基本の仕組み
ORDER BYを速くする基本は、データベースがインデックスの並び順をそのまま利用できる形に近づけることです。
インデックスは検索だけでなく、列の順番をたどる処理にも役立つ場合があります。
並び替え済みの順番で取り出せる状態とは
created_at順で一覧を表示したい場合、created_atを含むインデックスを順番にたどれれば、余計なソートを減らせることがあります。
すでに並んでいる道をたどるように行を取り出せるため、大量データでは差が出やすくなります。
インデックスがあっても必ず使われるわけではない理由
インデックスが存在していても、取得件数が多い場合や統計情報の判断によっては使われないことがあります。
インデックスを作ったあとは、作成した事実ではなく実行計画で実際に使われているかを確認します。
WHERE句とORDER BYを組み合わせるときの考え方
実務では、WHERE句で絞り込んだうえでORDER BYするSQLが多く使われます。
この場合はORDER BY列だけを見るのではなく、WHERE句の列とORDER BY句の列を一緒に考える必要があります。
WHEREで絞った後にORDER BYするSQLの基本
category_idで絞り込み、created_atの新しい順に並べるSQLでは、category_idで対象範囲を小さくしてからcreated_at順に取り出せる形が理想です。
created_at単体のインデックスより、category_idとcreated_atを組み合わせたインデックスが向く場合があります。
単体インデックスより複合インデックスが向くケース
WHERE句とORDER BY句がいつもセットで使われるなら、SQLの使われ方に合わせた複合インデックスを検討します。
| SQLの形 | 検討しやすいインデックス | 考え方 |
|---|---|---|
| WHERE category_id = ? ORDER BY created_at | category_id, created_at | カテゴリ内を作成日順で取り出す |
| WHERE user_id = ? ORDER BY updated_at | user_id, updated_at | ユーザー内を更新日順で取り出す |
| WHERE status = ? ORDER BY id | status, id | 状態ごとにID順で取り出す |
| WHERE category_id = ? AND status = ? ORDER BY created_at | category_id, status, created_at | 絞り込み後に日付順で取り出す |
絞り込みが弱いとORDER BYも重くなりやすい
WHERE句があっても、多くの行が条件に合う場合はORDER BYの負荷が残ります。
読み取り件数を減らせるほど並び替え対象も減りやすいため、条件の選び方も重要です。
複合インデックスは列順で効果が変わる
複合インデックスは、含める列だけでなく列の順番で効果が変わります。
同じ列を含んでいても、SQLの条件と並び順に合っていないと期待した性能になりません。
絞り込み列を前に置く基本パターン
基本的には、WHERE句で等価条件として使う列を前に置き、その後ろにORDER BYの列を置く形を検討します。
category_id = ? ORDER BY created_atであれば、category_id, created_atの順番が候補になります。
範囲条件がある場合に注意したいこと
created_at >= ? のような範囲条件を使う場合、複合インデックスの後ろの列をORDER BYに活かしにくくなることがあります。
範囲条件があるSQLでは、実行計画で追加ソートが残っていないかを特に確認します。
ASCとDESCが混在する場合の確認ポイント
一列のDESC指定なら通常のインデックスを逆順にたどれるDBMSもあります。
ただし、複数列でASCとDESCが混在する場合はDBMSやバージョンで扱いが変わるため、仕様と実行計画で判断します。
ORDER BYに関数や計算式を使うと遅くなりやすい
ORDER BYに関数や計算式を使うと、元の列に作ったインデックスの順番をそのまま利用しにくくなることがあります。
見た目を整えるための加工をORDER BY内で行うと、加工後の値で並べ直す必要が出る場合があります。
DATE_FORMATなどで並び替えると起きること
created_atをDATE_FORMATなどで変換してからORDER BYすると、created_atのインデックスをそのまま使いにくくなる場合があります。
件数が少ないうちは目立たなくても、データが増えるとソートの負荷が急に目立つことがあります。
SQL側で加工するかアプリ側で表示するかを考える
表示形式を整えるだけなら、並び替えは元の列で行い、表示加工はアプリ側や画面側で行う選択肢があります。
業務上どうしても計算結果で並べる必要がある場合は、DBMSごとの機能や保存列の利用も含めて検討します。
LIMITやOFFSETでORDER BYが軽くなるとは限らない
LIMITを付けると返却件数が少なくなるため、ORDER BYも必ず軽くなるように見えることがあります。
しかし、インデックスを使って先頭から必要件数だけ取れる場合と、全体を並べてから一部だけ返す場合では負荷が大きく違います。
LIMITだけでは全体ソートを避けられない場合がある
ORDER BYに合うインデックスを使える場合は、LIMITが性能改善につながりやすくなります。
一方でインデックス順を使えない場合は、多くの行を読み、並べ替えたあとにLIMIT件数だけ返す動きになることがあります。
OFFSETが大きいページほど重くなりやすい理由
OFFSETは指定した件数を読み飛ばしてから結果を返す仕組みです。
OFFSET 10000 LIMIT 20のようなSQLでは、画面上は20件でも内部では10000件以上をたどる可能性があります。
シーク法を検討したいケース
大量データのページングでは、前回取得したcreated_atやidを条件にして次の範囲を取得するシーク法を検討できます。
任意のページ番号へ直接移動する画面には向かないことがあるため、UI要件と合わせて判断します。
インデックスを追加する前に見直すチェックポイント
ORDER BYが重いときでも、最初の対応がインデックス追加とは限りません。
SQLの取得列、条件、既存インデックス、実行計画、データ分布を確認してから改善策を選びます。
| 確認順 | 見るポイント | 改善候補 |
|---|---|---|
| 実行計画 | ソートや全表走査が出ているか | SQLとインデックスの関係を見直す |
| 読み取り件数 | 返却件数より多すぎないか | WHERE句や条件を見直す |
| 既存インデックス | 似たインデックスがないか | 既存インデックスを活かす |
| SELECT列 | 不要な列を取っていないか | 取得列を絞る |
| データ分布 | 条件の偏りが大きいか | 統計情報や条件を確認する |
SELECT列や取得件数が多すぎないか
SELECTで不要な列まで取得していると、読み取り量や転送量が増えます。
一覧画面で使わない大きな列を取っている場合は、取得列を絞るだけでも改善につながることがあります。
条件に合う既存インデックスがないか
新しいインデックスを作る前に、既存インデックスで代用できないかを確認します。
似た複合インデックスがある場合は、追加ではなく統合や列順の見直しで対応できることがあります。
統計情報やデータの偏りを確認する
統計情報が古い場合やデータの偏りが大きい場合、期待と違う実行計画になることがあります。
インデックス設計だけでなく、統計情報とデータ分布も確認すると原因を見誤りにくくなります。
インデックスを増やしすぎるデメリット
ORDER BYを速くしたいからといって、インデックスをむやみに増やすのは避けたい対応です。
インデックスはSELECTを助ける一方で、更新処理、容量、保守性に影響します。
INSERTやUPDATEが重くなる可能性
インデックスが増えるほど、データを追加したり更新したりするたびに更新すべき構造が増えます。
更新頻度が高いテーブルでは、ORDER BY改善のためのインデックスが別の処理を遅くする可能性があります。
似た複合インデックスが増える問題
似た複合インデックスが増えすぎると、どのSQLのために作ったものか分かりにくくなります。
短期的な対処で追加したインデックスほど、後から棚卸しできるように目的を残しておくことが大切です。
使われていないインデックスを放置しない
使われていないインデックスは、SELECTを速くしないまま更新コストと容量だけを増やします。
削除前には月次処理や特定機能で使われていないかを確認し、影響範囲を見て判断します。
ORDER BYが重いSQLを直す実務手順
ORDER BYが重いSQLは、原因を切り分けながら順番に確認すると改善しやすくなります。
実行計画、読み取り件数、ソートの有無、WHERE句とORDER BY句の関係を見てから対応を選びます。
まず実行計画で原因を切り分ける
最初に、実行計画でソート処理が発生しているか、期待したインデックスが使われているか、読み取り件数が多すぎないかを確認します。
ソートが出ていないなら、遅さの原因はORDER BY以外にあるかもしれません。
SQLを書き換えるかインデックスを足すかを判断する
ORDER BYに関数を使っているなら元の列で並べられないかを考えます。
WHERE句とORDER BYの組み合わせが固定されているなら、複合インデックスを追加する価値があります。
改善前後の処理時間と実行計画を比較する
改善後は、処理時間だけでなく実行計画がどう変わったかも確認します。
読み取り件数、ソートの有無、利用インデックスを比較すると、改善できた理由が分かりやすくなります。
よくある質問(Q & A)
ORDER BYとインデックスの関係では、よくある誤解がいくつかあります。
ORDER BY列だけを見て判断せず、SQL全体、DBMSの仕様、画面要件、データ量を合わせて考えます。
ORDER BYの列にインデックスを作れば必ず速くなりますか?
必ず速くなるわけではありません。
ORDER BY列だけでなく、WHERE句との組み合わせや取得件数、データ分布が関係します。
ORDER BY DESCではDESC用インデックスが必要ですか?
一列のDESC指定であれば、DBMSによっては通常のインデックスを逆順にたどれることがあります。
複数列でASCとDESCが混在する場合は、対象DBMSの仕様と実行計画を見て判断します。
ORDER BYを外せば速くなりますか?
ORDER BYを外せばソート処理がなくなるため、速くなる可能性はあります。
ただし、必要な表示順がある画面でORDER BYを外すと取得順が保証されなくなるため注意が必要です。
複合インデックスはどこまで増やしてよいですか?
複合インデックスは、よく使われる重要なSQLを基準に必要最小限で設計します。
追加する前に既存インデックスで対応できないか、列順を統合できないか、利用頻度が高いかを確認します。
まとめ
ORDER BYが重い原因は、並び替え対象の件数だけではありません。
WHERE句との組み合わせ、複合インデックスの列順、関数の使用、LIMITやOFFSETの使い方によって処理時間は変わります。
特に大切なのは、インデックスの並び順を使えているか、余計なソートが発生していないか、読み取り件数が多すぎないかを実行計画で確認することです。
インデックスを追加する場合も、更新コストや容量、保守性への影響を考える必要があります。
ORDER BYの改善は、実行計画で原因を切り分け、SQLの書き方とインデックス設計をセットで見直すことが近道です。