2
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?

More than 1 year has passed since last update.

SQLを速くするために学んだこと

Posted at

以前のアカウントがどこかへいってしまったので作り直しをしました。
最近業務でSQLを触る機会が多く、少し勉強をしたのでメモ書き程度ですが書いてみました。

サブクエリはINよりもEXISTSを使用する

-- 遅い
SELECT * 
FROM ClassA
WHERE id IN (SELECT id FROM ClassB);

-- 速い
SELECT *
FROM CLASSA A
WHERE EXISTS
	(SELECT *
		FROM ClassB B
		WHERE A.id = B.id);
  • idにindexが貼られていればClassBは見に行かずインデックスを参照するのみで済む
  • EXISTSは1行でも一致する行を見つけるとここで検索を打ち切るから全検索にならない

サブクエリよりも結合を使う

SELECT A.id, A.name
FROM ClassA A
INNER JOIN ClassB B
ON A.id = B.id
  • どちららかのindexが必ず効いてくる

ソートを回避する

GROUP BY、ORDER BY、DISTINCTなどソートが発生するものは注意が必要になる

重複を気にしなくていい場合はUNIONの代わりにUNION ALLを使うようにする

SELECT *
FROM ClassA
UNION ALL
SELECT * FROM ClassB

DISTINCT(重複を省く)をEXISTSで代用する

-- 遅い
SELECT DISTINCT I.no
FROM Items I 
INNER JOIN SalesHistory SH ON I.no = SH.no

-- 速い
SELECT I.no
FROM Items I 
WHERE EXISTS (
	SELECT * FROM SalesHistory SH
	WHERE I.no = SH.no
)

MAX/MINでINDEXを使う

-- indexを利用していない
SELECT MAX(item)
FROM Items;

-- indexを利用している
SELECT MAX(item_no)
FROM Items;
  • item_noは主キーなのでより効果的にindexを使用できている

WHERE句出かけるものはHAVING句に書かない

以下のSQLはどちらも同じ結果になるが、後者の方が速い

-- 遅い
SELECT sales_date, SUM(quanitity)
FROM SalesHistory
GROUP BY sales_date
HAVING sale_date = '2023-09-01';

-- 速い
SELECT sales_date, SUM(quanitity)
FROM SalesHistory
WHERE sales_date = '2023-09-01'
GROUP BY sales_date;
  • GROUP BYはWHEREで指定された後に行うのでなるべくレコード数を絞り込んだ方がいい
  • WHEREでindexをを利用できる

indexを正しく使おう

indexを貼ってはいるものの、理解した上で使わないと上手く使えていないこともある

加工を行なっている

計算式を使っている場合、左辺ではなく右辺で計算するようにする

-- indexを利用していない
SELECT *
FROM SomeTable
WHERE col_1 * 3 > 100;

-- indexを利用している
SELECT *
FROM SomeTable
WHERE col_1 > 100 / 3;

NULLが存在する

基本的にNULLが存在しているカラムには、indexを貼るべきではないと考えている
以下は応急処置程度で考えるといい

-- index利用していない
SELECT *
FROM SomeTable
WHERE col_1 IS NOT NULL

-- indexを利用している
SELECT *
FROM SomeTable
WHERE col_1 > 0;

否定系を使っている

以下のような否定系にはindexが効かない

  • <>
  • !=
  • NOT IN

複合indexの順番を間違えている

col_1,clo_2,clo_3の順番にindexが貼られているとする

-- indexを利用していない
SELECT *
FROM SomeTable
WHERE col_2 = 30
AND clo_1 = 10
AND clo_3 = 30;

SELECT *
FROM SomeTable
WHERE col_1 = 10
AND clo_3 = 20;

SELECT *
FROM SomeTable
WHERE col_2 = 30
AND clo_3 = 30;

-- indexを利用している
SELECT *
FROM SomeTable
WHERE col_1 = 10
AND clo_2 = 20
AND clo_3 = 30;

SELECT *
FROM SomeTable
WHERE col_1 = 10
AND clo_2 = 20;

感想

EXISTSは知ってはいたもののどうゆう挙動するのかあまりわかっていないので使っていなかった。サブクエリをする上ではEXISTSを意識してみようと思う。
ソートには負荷がかかるのは知っていたが、GROUP BYなど結構よく使う。
INDEXについて今までINDEXついているものを検索に使おうと心がけていたが、順番や否定系には使えないことは学びだった。
業務でかなりのレコード数があるテーブルでSQLを書くときに、重くて取得できないことがよくある。こうした小さな工夫の組み合わせでパフォーマンス向上させられるといいなと思うので、また勉強してみようと思う。

参考書籍: 達人に学ぶSQL徹底指南書 第2版 初級者で終わりたくないあなたへ

2
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
2
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?