チューニングの際の確認用と、無自覚にボトルネックを生み出さないよう、自戒のため記録しておく。
検索の効率はどうか
同じ結果が得られるのに、効率の悪い検索をしている箇所を撲滅する。
無駄にテーブルを見る回数を減らすこと。
サブクエリ用いるとき、IN
,EXISTS
(もしくはJOIN
)を適切に使えているか
ただし最近のDBでは処理時間が改善されあまり差がなくなっているので、あまり変わらないことが多い。
また、IN
の方がコードのわかりやすさは勝るので、速度が変わらなければIN
でもOK。
IN
を使う場合
絞り込むテーブルの行数が、サブクエリで絞り込まれるレコード数より多い場合
/* table1 > table2 */
SELECT * FROM table1 WHERE id IN (SELECT id FROM table2;)
# table2からレコードを取得してループを回して、table1と紐付ける
# table1はインデックスが効くので、データが多くても良い
EXISTS
を使う場合
絞り込むテーブルの行数が、サブクエリで絞り込まれるレコード数より小さい場合
/* table1 < table2 */
SELECT * FROM table1 WHERE EXISTS (SELECT id FROM table2 WHERE table2.id = table1.id);
# table1からレコードを取得してループを回して、table2と紐付ける
※ もし結合キー(上記例ではid)にインデックスがはられていれば、tableBの中身を見にいかず、インデックスだけ見るので、JOIN
の方が速いこともある。
ソートを回避できているか
ソートがメモリ上で行われるならまだいいが、ストレージが使われるようになると急激に遅くなる。
実行前にWHEREとかで絞り込み、早い段階でデータ量を減らすこと。
もしくは表示件数を減らすとかするのもあり。
DISTINCT
をEXISTS
で代用できないか
DISTINCT
もソートを行うため、EXISTS
に書き換えることでソートを回避できる。
/* 遅い */
SELECT DISTINCT customer_id
FROM customer
INNER JOIN company
ON company_id = company_id
/* 速い */
SELECT customer_id
FROM customer
WHERE EXIATS (SELECT * FROM company WHERE company_id = company_d);
HAVING
をWHERE
で代用できないか
HAVING
は絞り込んだ結果に対して実行されるので、そうではなくWHERE
で絞り込んでからソートするべき。
中間テーブルを無駄に作っていないか
- 集約した結果に対する条件は、中間テーブルを作らずHAVING句を活用する
- 集約と結合を同時に行う場合は、結合を先に行う
SQLの実行回数を減らせないか
実行ごとにサーバー間で通信が発生してしまうので、一回で実行した方が速いことが多い
ループの中で回してるSQLがたまにあるので、ループの外に出してあげる
インデックスを活用できているか
どうしようもなければ
- あえてカラムを増やし(非正規化し)、結合が発生しないようにする。(テーブルのデータ量が増えるのでケースバイケース)
- リアルタイムで実行計画が必要ない場合、夜間に実行計画を格納し、集計結果を別テーブルに格納する。
- 重いSQLはユーザーが使用しない時間に実行する。
-
LIMIT
をつけて実行する。表示する件数を絞る。