0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【メモ】SQLパフォーマンスチューニング

Last updated at Posted at 2025-06-25

参考書籍:達人に学ぶSQL徹底指南書

レスポンスが遅くなる主原因

・メモリ配分が悪い
・ストレージ構成が不適切

最大のボトルネックになり得るのは、ストレージへのアクセス。
そのため、高速アクセスが可能なストレージの導入がパフォーマンス向上に重要ある。その上で、SQLを工夫することが効果を発揮する。

1、効率の良い検索を利用

サブクエリを引数に取る場合は、INではなくEXISITSを使用する

理由:
INを使う場合、内部の動きとしてまずサブクエリを実行し、一時的なワークテーブルに格納した上で、全件検索をする。一方、EXISTSはワークテーブルを作らず、1行でも条件に合致する行を見つけたらそこで検索を打ち切る。

補足:
将来的にはINも各種DBMSで性能が改善される可能性がある。

2、ソートを回避する

ソートがメモリ上で行われている間は良いが、足りずにストレージを使うようになるとパフォーマンスが落ちる。無駄なソートは極力避けるべし。

ソートが発生する演算
GROUP BY
ORDER BY
集約関数 SUM,COUNT,AVG,MAX,MIN
DISTINCT
集合演算子 UNION,INTERSECT,EXCEPT
ウィンドウ関数 RANK,ROW_NUMBERなど

対策:集合演算子のALLオプションをうまく使う
例:UNION ALL

理由:
ALLを使わない場合、重複排除のためのソートを使う。
もし、重複しても良い場合はALLを使おう

3、極値関数でインデックスを使う

引数の列にインデックスが存在する場合は、MIN、MAXを使うとインデックスのスキャンだけで済ませ日表への検索を回避できる。

ポイント:
インデックスが存在する列を指定する。主キーのユニークインデックスだとより効果的。

4、WHERE区で書ける条件はHAVING句には書かない

理由:
GROUP BY句による集約はソートやハッシュの演算を行うので、事前に行数を絞り込んだ方が良い。

5インデックスが使われる書き方をする

5-1.検索条件の右側で式を用いるとインデックスが使われる

-- 悪い書き方
SELECT * 
  FROM テーブル名 
 WHERE col_1 * 1.1 > 100

-- 良い書き方
SELECT * 
  FROM テーブル名 
 WHERE col_1 > 100 / 1.1

5-2.インデックス列にNULLが存在する場合

インデックスを使いながら、IS NOT NULLの条件で実行したい場合、
下記で代用するのも手

SELECT * 
  FROM テーブル名 
 WHERE col_1 > 0

5-3.否定系を使う場合、インデックスを使用できない

以下のような否定系はインデックス使用不可
<>
!=
NOT IN

5-4.複合インデックスの場合、列の順番を正しくする

例:(col_1,col_2,col_3)の順で 複合インデックスが張られている場合


-- 正しい
SELECT * 
  FROM テーブル名 
 WHERE col_1 = 10 
   AND col_2 = 20 
   AND col_3 = 30

-- 正しい
SELECT * 
  FROM テーブル名 
 WHERE col_1 = 10 
   AND col_2 = 20

-- 誤り
SELECT * 
  FROM テーブル名 
 WHERE col_2 = 20 
   AND col_3 = 30

最後クエリのクエリではcol_2から指定しており、この場合、col_1をスキップするため、スキップスキャンが起こる。

5-5.LIKE句では前方一致のみインデックスが使われる

インデックスが使える
SELECT * 
  FROM テーブル名 
 WHERE col_1 LIKE 'a%'

インデックスが使えない
SELECT * 
  FROM テーブル名 
 WHERE col_1 LIKE '%a'

SELECT * 
  FROM テーブル名 
 WHERE col_1 LIKE '%a%'

5-6.暗黙の型変換を行うとインデックスが使えない

暗黙の型変換はインデックスが使えず、さらにオーバーヘッドも発生する。

-- col_1が文字型で定義されている場合

-- OK(明示的に型変換している)
SELECT * 
  FROM テーブル名 
 WHERE col_1 = '10'

SELECT * 
  FROM テーブル名 
 WHERE col_1 = CAST(10, AS CHAR(2))

-- NG (型変換が発生する)
SELECT * 
  FROM テーブル名 
 WHERE col_1 = 10 

6、中間テーブルを減らす

中間テーブルは以下のデメリットがある
・データを展開するためにメモリ(またはストレージ)を消費する
・元テーブルに存在しているインデックスを使うことが難しい

6-1.代替としてHAVING句を活用する

集約した結果に対する条件はHAVING句を使うのが原則。

6-2.INで複数のキーを利用する場合、一箇所に集める

-- 2箇所でINを使っている

SELECT id, pref, city
  FROM Address
 WHERE state IN (サブクエリ)
   AND city IN (サブクエリ)

-- 1箇所に集約する
SELECT id, 
       pref, 
       city
  FROM Address
 WHERE id || state || city IN (サブクエリ)

6-3.集約よりも結合を先に行う

集約と結合を併用する場合、中間テーブルを省略するために、集約の間に結合しておく。

6-4.複雑なビューはパフォーマンスが良くない

集約関数や集合演算子がビュー定義に含まれる場合、パフォーマンスに影響する。
そのような場合、代替としてマテリアライズドビューを使用するとよさそう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?