LLaravel Bootcamp

Day 4EloquentとリレーションとN+1問題

クエリビルダとコレクションの違い

絞り込みをDB側でやるか、PHP側でやるか。性能の基礎となる「どこで絞るか」の意識を持つ。

クエリビルダとコレクション

Eloquentには、似ているようで働く場所が違う2つの世界があります。「クエリビルダ」は、まだDBに投げる前のSQLを組み立てる段階で、whereやorderByはここでSQLの条件になります。一方「コレクション」は、getなどでDBから取り出した後のデータ(PHPのメモリ上の配列のようなもの)で、そこでのfilterやsortはPHP側の処理になります。

補足

見分け方の目安: getやall、findで結果を取り出す前がクエリビルダ(SQLになる)、取り出した後がコレクション(PHPで処理される)です。

どこで絞るかで性能が変わる

同じ「公開済みの投稿だけ欲しい」でも、DB側で絞るかPHP側で絞るかで負荷が大きく変わります。全件をPHPに読み込んでから絞ると、DBもメモリも無駄に使います。

悪い例(全件取ってからPHPで絞る)

全件をメモリに載せてからfilter。DB転送量もメモリも無駄。

PHP
// 全投稿をDBから取り出す(公開・非公開すべて)
$posts = Post::all();

// PHP側(コレクション)で絞り込む
$published = $posts->filter(function ($post) {
    return $post->is_published;
});
// 100万件あれば100万件を読み込んでから捨てることになる

良い例(DB側で絞ってから取り出す)

whereで条件をSQLに乗せ、必要な行だけDBから受け取る。

PHP
// DB側(クエリビルダ)で絞ってから取り出す
$published = Post::where('is_published', true)->get();

// 必要な行だけがメモリに載る。転送量も少ない
SQL
-- 条件が WHERE に乗るので、DBが必要な行だけ返す
SELECT * FROM posts WHERE is_published = 1;
良い例で発行されるSQL(DBが絞ってくれる)

なぜ「DB側で絞る」のが基本なのか

DBは大量データの絞り込み・並べ替えに最適化された専用エンジンで、インデックスも使えます。PHP側で絞ると、まず全件をネットワーク経由で受け取り、メモリに載せ、そこから捨てる、という無駄が発生します。『絞り込みや集計はできるだけDBに任せ、PHPには必要な分だけ持ってくる』——これが性能設計の基礎です。

件数の多いテーブルへの向き合い方

  • 一覧はpaginateでページ分割し、一度に全件を読まない。
  • 絞り込み条件(where)・並べ替え(orderBy)はできるだけクエリビルダ側=SQLに寄せる。
  • 件数だけ欲しいならcount()、存在確認だけならexists()を使い、全件取得を避ける。
  • 集計(合計・平均)もsum()やavg()でDB側に任せる。
  • 大量データを1件ずつ処理するときはchunkやcursorでメモリを守る。

落とし穴

コレクションのメソッド(filter・sort・whereなど)は便利ですが、それはすでにメモリに載ったデータへの操作です。「Post::all()->where(...)」のように書くと、先に全件を取ってしまうので、DBで絞る「Post::where(...)->get()」とは負荷が全く違います。メソッド名が似ているぶん取り違えやすいので注意しましょう。

面接ではこう言う

「クエリビルダはSQLになる前、コレクションは取り出した後のPHP上のデータで、絞り込みをどちらでやるかで性能が変わります。私は絞り込み・並べ替え・集計はできるだけwhereやcount、sumでDB側に寄せ、PHPには必要な分だけ持ってくる方針です。一覧はpaginateで全件取得を避けます」と言えると、性能を意識していると伝わります。

読んでも腑に落ちない所は、AIに噛み砕いてもらいましょう。

理解度チェック

答えられれば、面接で説明できる状態に一歩近づきます。

Q1

「Post::all()->filter(...)」と「Post::where(...)->get()」の違いとして、最も適切なものはどれか。

Q2

「絞り込みはできるだけDB側でやるべき」と言われる理由を説明せよ。

記述式です。まず自分の言葉で答えを考えてから、模範解答を開いて自己採点してください。