第10回: サブクエリとCTEの基本 — 複雑なSQLを「読める」形に分解する
サブクエリを多重にネストしたSQLは、どこが何を返しているのか追いにくく、バグの原因も特定しづらくなります。3段ネストのサブクエリは、書いた本人でも翌週には読めないことがあります。
Category
サブクエリを多重にネストした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は、アプリ側のバグがそのままデータ破損につながります。
DB設計でよくある失敗は、文字コードを指定せずにテーブルを作り、絵文字が入らなかったり日本語が文字化けしたりすること。あるいはカラムの型を雑に決めて、後から ALTER TABLE で大量データを抱えたまま変更しようとして詰まること。
ほぼすべてのWebサービス——SNS・ECサイト・業務システム——は、裏側にデータベースを持っています。その中で世界シェアトップを争うのが MySQL です。