【Laravel】withCount・withSumの使い方と注意点|集計クエリを高速化するテクニック

【Laravel】withCount・withSumの使い方と注意点|集計クエリを高速化するテクニック Laravel

Laravelでは、リレーション先の件数や合計値を効率よく取得するために withCountwithSum といった便利な集計メソッドが用意されています。

この記事では、withCount・withSumの使い方とSQLパフォーマンスに配慮した実装方法、注意すべき落とし穴までを詳しく解説します。

withCountとは?

withCount は、リレーション先の件数を取得し、モデルのプロパティとして扱えるようにするメソッドです。

基本的な使い方

たとえば、Userモデルがpostsリレーションを持っているとき、ユーザーごとの投稿数を取得するには以下のようにします:

User::withCount('posts')->get();

この場合、取得したユーザーモデルには posts_count プロパティが追加されます。

$user->posts_count;

複数リレーションの件数をまとめて取得

User::withCount(['posts', 'comments'])->get();

このようにすれば、posts_countcomments_count が同時に取得できます。

withSumとは?

withSum は、リレーション先の合計値(sum)を集計して取得するためのメソッドです。主に数値フィールドの合計が必要なケースで活用します。

例:注文ごとの合計金額を取得

User::withSum('orders', 'total_price')->get();

この場合、$user->orders_sum_total_price の形で合計金額が取得できます。

複数項目の合計を取得したいとき

User::withSum([
  'orders as total_items_sum' => function ($query) {
    $query->selectRaw('SUM(quantity)');
  },
])->get();

as句を使えば、任意のプロパティ名で取得することも可能です。

条件付きのカウント・集計

リレーションに条件を加えたい場合は、配列形式でクロージャを指定できます。

User::withCount([
  'posts' => function ($query) {
    $query->where('status', 'published');
  }
])->get();

このようにすると、「公開済みの記事数」のみがカウントされます。

パフォーマンス面のメリット

withCount・withSumは、N+1問題を回避しつつ、サブクエリで集計を行うため非常に効率的です。

select users.*, 
  (select count(*) from posts where posts.user_id = users.id) as posts_count
from users;

このようなSQLが生成され、集約関数が1クエリで実行される点がポイントです。

注意点:with()との併用との違い

with('posts') でリレーション全体を読み込むのとは異なり、withCount('posts') は件数のみを取得します。

両方が必要な場合は、以下のように併用できます:

User::with('posts')->withCount('posts')->get();

ただし、同時に大量のリレーションを読み込むとメモリ使用量が増えるため、必要に応じて最適化を意識しましょう。

withAvg・withMax・withMinも使える

Laravel 8以降では withAvgwithMaxwithMin も使えます。

User::withAvg('orders', 'score')->get();
User::withMax('orders', 'total_price')->get();
User::withMin('orders', 'delivery_days')->get();

統計値を1クエリで取得したい場面では積極的に活用できます。

まとめ

withCount / withSum / withAvg などの集計系メソッドは、リレーションの情報を軽量かつ高速に取得できる強力な手段です。

  • withCount で件数を取得(posts_count)
  • withSum で合計値を取得(orders_sum_total_price)
  • 条件付き集計や as句で柔軟にプロパティ名を変更可能

ビュー側でループして集計するのではなく、クエリレベルで集計処理を完結させることで、処理の高速化と保守性の向上が実現できます。

一覧表示・管理画面・APIレスポンスなど、集計が求められる場面で積極的に活用していきましょう。