章: 第2章: 実務クエリ基礎
「投稿一覧を表示するたびにSQLが100本走る」——心当たりはありませんか?
投稿一覧を取得した後、各投稿の著者名を取るためにループで SELECT * FROM users WHERE id = ? を繰り返す——これがN+1問題の典型例です。100件の投稿があれば101本のSQLが走り、データが増えるほどレスポンスが悪化します。
JOINを使えば、複数テーブルの情報を1本のSQLで取得でき、N+1を根本から防げます。
INNER JOIN と LEFT JOIN——使い分けの判断軸
JOIN の種類を間違えると、取得したかったデータが消えたり、NULLが混入したりします。選択基準はシンプルです。「相手側のデータが必ず存在する」ならINNER JOIN、「相手側がなくても親を残したい」ならLEFT JOIN です。
INNER JOIN vs LEFT JOIN の比較
| 観点 | INNER JOIN | LEFT JOIN |
| 結果に含まれる行 | 両テーブルに一致する行のみ | 左テーブルの全行(右がなければNULL) |
| 典型的な用途 | 投稿とその著者(著者は必ず存在) | ユーザーと投稿(投稿がないユーザーも表示) |
| NULL混入 | なし | 右テーブルのカラムがNULLになり得る |
| 間違えたときの症状 | 右がないレコードが消える | 想定外のNULLでアプリ側エラー |
チェックポイント: LEFT JOINの結果は右テーブルのカラムがNULLになる行を含みます。アプリ側でNULLチェックを忘れると
undefinedやnull pointer系のエラーが発生します。取得直後にWHERE p.id IS NOT NULLで存在確認するか、アプリ側でガード処理を入れましょう。
実際のコードのサンプル
SELECT p.id, p.title, u.name AS author_name
FROM posts p
INNER JOIN users u ON p.user_id = u.id;
SELECT u.id, u.name, p.title
FROM users u
LEFT JOIN posts p ON p.user_id = u.id;
まとめ & 次のステップ
- INNER JOINは両テーブルに一致する行のみを返す。片方にデータがない行は結果から除外される
- LEFT JOINは左テーブルの全行を返す。右テーブルに一致がない場合はNULLで補完される
- JOINのON句に使うカラムにはインデックスを張ることで、大量データでも高速に結合できる
- N+1はJOINやIN句による一括取得で解消する。ループ内のSQLは即疑うべきシグナル
次回は 「GROUP BYと集計関数」 を学びます。売上合計・投稿数カウントなど、レポート系機能に欠かせない集計クエリの書き方を確認します。