第12回: 複合インデックスの順序 — 列の並びひとつで速度が激変する
「インデックスを追加したはずなのに、クエリが遅い」——そんな経験はありませんか?実は複合インデックスは、列の順序を間違えるとほとんど機能しません。正しい順序で設計するだけで、クエリが劇的に速くなるケースがあります。この回では複合インデックスの順序の考え方を整理していきます。
Year
「インデックスを追加したはずなのに、クエリが遅い」——そんな経験はありませんか?実は複合インデックスは、列の順序を間違えるとほとんど機能しません。正しい順序で設計するだけで、クエリが劇的に速くなるケースがあります。この回では複合インデックスの順序の考え方を整理していきます。
テスト環境では一瞬だったクエリが、本番の数十万件データでは5秒以上かかる。そういう事態に直面したとき、「インデックスを貼ればいいんじゃないか」と感覚で対応するのは危険です。闇雲にインデックスを追加すると、INSERT/UPDATE のたびにインデックスの更新コストが増え、むしろ書…
サブクエリを多重にネストしたSQLは、どこが何を返しているのか追いにくく、バグの原因も特定しづらくなります。3段ネストのサブクエリは、書いた本人でも翌週には読めないことがあります。
WHERE postcount >= 10 と書きたくなりますが、postcount は集計結果なので WHERE の時点ではまだ存在しません。WHERE はグループ化・集計の前に評価されるため、集計後の値を条件にするには別の構文が必要です。
全投稿を取得してPHPの配列で count() するコードは、データが増えるほどメモリと処理時間を消費します。1万件のレコードを全件取得してアプリ側で集計するのは、DB本来の仕事をわざわざ横取りしている状態です。
投稿一覧を取得した後、各投稿の著者名を取るためにループで SELECT FROM users WHERE id = ? を繰り返す——これがN+1問題の典型例です。100件の投稿があれば101本のSQLが走り、データが増えるほどレスポンスが悪化します。
一覧画面でよくある失敗が「ORDER BYを書かずにLIMITだけ指定する」パターンです。ORDER BYなしのLIMITは、MySQLが内部でどの行を返すかを保証しないため、同じクエリでも実行するたびに結果の順番が変わるという不具合が起きます。さらに、データ量が増えると OFF…
SELECT で全件取得してアプリ側でフィルタリング——これは開発初期によくある実装です。しかしデータが10万件・100万件と増えるにつれ、レスポンスが劣化し、不要なデータ転送でメモリも圧迫されます。
UPDATE users SET name = ‘admin’ ——WHERE句を忘れた瞬間、全ユーザーの名前が書き変わります。更新系SQLのミスはデータ欠損に直結し、バックアップからの復元が必要になることも。
ユーザーを削除したのに、そのユーザーの投稿が残り続ける。存在しないユーザーIDを持つ注文レコードが生まれる。外部キー制約がないDBは、アプリ側のバグがそのままデータ破損につながります。