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 など)の導入を検討してください。
まとめ
これらを意識するだけで、多くのクエリは改善できます。改善後は必ず EXPLAIN で効果を確認しましょう。