MySQL で SELECT クエリを高速化する5つのテクニック

SELECT クエリが遅いとき、闇雲にインデックスを追加するだけでは解決しないことがあります。ここでは、実践的な高速化テクニックを5つ紹介します。

1. 必要なカラムだけを取得する

SELECT * は便利ですが、パフォーマンスの観点では避けるべきです。

悪い例

SELECT * FROM users WHERE id = 100

良い例

SELECT id, name, email FROM users WHERE id = 100

不要なカラムを取得すると、ネットワーク転送量が増え、メモリも余計に消費します。特に BLOB や TEXT 型のカラムを含むテーブルでは、影響が顕著になります。

また、カバリングインデックスの恩恵を受けるためにも、取得カラムを絞ることが重要です。

2. LIMIT で取得件数を制限する

全件取得が不要な場面では、必ず LIMIT を付けましょう。

-- ページネーション
SELECT id, title FROM articles ORDER BY created_at DESC LIMIT 20 OFFSET 0;

-- 存在確認だけなら1件で十分
SELECT id FROM users WHERE email = 'test@example.com' LIMIT 1;

存在確認に COUNT(*) を使うのは非効率です。該当行があるかどうかだけ知りたいなら、LIMIT 1 で1行取得できるかを確認するほうが速くなります。

3. OR を UNION に書き換える

複数条件を OR で結合すると、インデックスが効かないことがあります。

インデックスが効きにくい

SELECT * FROM products WHERE category_id = 1 OR brand_id = 5

インデックスが効きやすい

SELECT * FROM products WHERE category_id = 1 UNION SELECT * FROM products WHERE brand_id = 5

UNION に書き換えることで、各クエリが個別のインデックスを使えるようになります。ただし重複排除のコストがあるため、重複を許容できるなら UNION ALL を使いましょう。

4. サブクエリを JOIN に変換する

WHERE 句のサブクエリは、行ごとに実行される可能性があり、非効率になりがちです。

-- サブクエリ版(遅い可能性あり)
SELECT * FROM orders 
WHERE user_id IN (SELECT id FROM users WHERE status = 'active');

-- JOIN版(効率的)
SELECT o.* FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE u.status = 'active';

MySQL のオプティマイザは賢くなっていますが、明示的に JOIN で書いたほうが意図が伝わりやすく、実行計画も安定します。

5. 計算や関数をカラムに適用しない

WHERE 句でカラムに関数を適用すると、インデックスが使えなくなります。

インデックスが効かない

SELECT * FROM logs WHERE DATE(created_at) = ‘2024-01-15’

インデックスが効く

SELECT * FROM logs WHERE created_at >= ‘2024-01-15’ AND created_at < ‘2024-01-16’

同様に、文字列の前方一致以外の LIKE もインデックスが効きません。

前方一致(インデックス有効)

WHERE name LIKE ‘yamada%’

部分一致(インデックス無効)

WHERE name LIKE ‘%yamada%’

部分一致検索が必要な場合は、全文検索インデックスや外部の検索エンジン(Elasticsearch など)の導入を検討してください。

まとめ

SELECT * を避け、必要カラムだけ取得
LIMIT で取得件数を制限
OR は UNION への書き換えを検討
サブクエリより JOIN を優先
WHERE 句のカラムに関数を適用しない

これらを意識するだけで、多くのクエリは改善できます。改善後は必ず EXPLAIN で効果を確認しましょう。