Q1インデックスのない列でORDER BYをしたとき、実行計画に現れるのはどれですか。
ORDER BY / GROUP BY をインデックスで速くする
この記事は、基礎から複雑なSQL,SQLチューニングまでSQLの実践的なスキルを1からマスターする「SQL入門講座の一部」です。
ORDER BY や GROUP BY、DISTINCT で発生する並べ替え作業を、対象列にインデックスを作って省略する書き方を確かめます。
並び替えの一時ソートをインデックスで消す
ORDER BYで並び替えるとき、対象列にインデックスが無いと、いったん全行を取り出してから並べ替える作業が必要になります。
この一時的な並べ替えを実行計画ではUSE TEMP B-TREE FOR ORDER BY(並べ替えのための一時的な作業用構造を作る)と表示します。
ここで、並び替えたい列にインデックスを作ると話が変わります。
インデックスは作った時点でその列の順に並んでいます。
インデックスの並びを使えばすぐに結果が得られます。
一時ソートが不要になり、計画からUSE TEMP B-TREEが消えます。
一時ソートは全 5 万行をいったん作業用構造に並べ直す処理で、行数に比例して重くなります。
LIMIT 10で最終的に 10 行しか返さなくても、上位 10 行を決めるには全行を並べ替える必要があるため負担は変わりません。
この記事では、「ソートをインデックスで消す」のを試して見ましょう。
-- amount で並べた上位 10 件を取り出す例
-- インデックスがあれば並びをたどるだけで済む
DROP INDEX IF EXISTS ix_demo;
CREATE INDEX ix_demo ON perf_sales(amount);
EXPLAIN QUERY PLAN
SELECT sale_id, amount
FROM perf_sales
ORDER BY amount
LIMIT 10;
GROUP BY もインデックスの並びでまとめられる
GROUP BYも、グループ化のために値が同じ行を隣り合わせる必要があります。
インデックスが無いと一時ソートでUSE TEMP B-TREE FOR GROUP BYが発生します。
グループ化する列にインデックスを作ると、同じ値の行がインデックスの並びの中ですでに連続しているため、その並びを順にたどるだけでグループを切り出せます。
たとえばGROUP BY emp_idならemp_idにインデックスを作ると、emp_idごとのまとまりが並びの中で連続しているため、一時ソートなしで集計できます。
集計対象の列も一緒にインデックスへ含めれば、テーブル本体へ戻らずに済む形(前記事のインデックスオンリースキャン)と組み合わせられます。
-- region ごとの件数を集計する例
-- region のインデックスがあれば GROUP BY の一時ソートが不要
DROP INDEX IF EXISTS ix_demo;
CREATE INDEX ix_demo ON perf_sales(region);
EXPLAIN QUERY PLAN
SELECT region, COUNT(*)
FROM perf_sales
GROUP BY region;
DISTINCT も同じ仕組みで一時ソートを消せる
DISTINCT(重複を取り除いて一意な値だけを返す指定)も、重複判定のために値を並べる必要があり、インデックスが無いと一時ソートでUSE TEMP B-TREE FOR DISTINCTが発生します。
仕組みはGROUP BYと同じで、対象の列にインデックスがあると同じ値が並びの中で連続しているため、隣どうしを比べるだけで重複を取り除けます。
SELECT DISTINCT product FROM perf_salesのように 1 列の一意な値を取り出すなら、productにインデックスを作ると一時ソートが不要になります。
並び替え・グループ化・重複排除は、いずれも「値を並べる必要がある処理」という点で共通しており、対象列のインデックスで一時ソートを避けられます。
-- region の一意な値を取り出す例
-- region のインデックスで DISTINCT の一時ソートを回避
DROP INDEX IF EXISTS ix_demo;
CREATE INDEX ix_demo ON perf_sales(region);
EXPLAIN QUERY PLAN
SELECT DISTINCT region
FROM perf_sales;
理解度チェック
まずは1問ずつ答えてみましょう。
Q2GROUP BY emp_idの一時ソートをインデックスで回避できるのはなぜですか。
Q3ORDER BY / GROUP BY / DISTINCT の一時ソートをインデックスで回避できる共通の理由はどれですか。