Laravelのdistinct完全ガイド:使い方とunique・groupByとの違い

基本文法・構文ガイド

Laravelで重複データを除外したい場合、Query Builder / Eloquent では distinct()、Collection では unique() を使います(集計が必要ならgroupBy())。この記事では、それぞれの使い方と使い分けを具体的なコード例とともに解説します。

distinctの基本用途

distinct() は SQL の SELECT DISTINCT を発行し、データベースレベルで重複行を除外します。

// 全カラムで重複する行を除外
$users = DB::table('users')->distinct()->get();

全カラムが一致する行のみ重複とみなします。特定のカラムだけを対象にしたい場合は、select() と組み合わせる必要があります。

Query Builderでのdistinct使い方

クエリビルダーで distinct() を使う基本パターンを紹介します。

全カラムの重複排除

$users = DB::table('users')->distinct()->get();

特定カラムの重複排除

特定カラムのユニークな値だけを取得するには、select() と組み合わせます。

// メールアドレスの重複を除外して取得
$emails = DB::table('users')->select('email')->distinct()->get();

// 複数カラムの組み合わせで重複排除
$locations = DB::table('users')
    ->select('city', 'prefecture')
    ->distinct()
    ->get();

条件付きの重複排除

// アクティブユーザーのメールアドレスを重複なしで取得
$emails = DB::table('users')
    ->select('email')
    ->where('active', 1)
    ->distinct()
    ->get();

件数の取得

// 重複を除いた件数
$count = DB::table('users')->select('email')->distinct()->count('email');

Eloquentでのdistinct使い方

Eloquentモデルでも同様に distinct() を利用できます。

基本的な使い方

// モデルで特定カラムの重複排除
$emails = User::select('email')->distinct()->get();

スコープ・joinとの組み合わせ

// スコープとの組み合わせ
$emails = User::active()->select('email')->distinct()->get();

// joinを使った例
$cities = User::join('profiles', 'users.id', '=', 'profiles.user_id')
    ->select('profiles.city')
    ->distinct()
    ->get();

Eloquentモデルを使うことで、アクセサや型キャストなどのモデル機能もあわせて利用できます。

Collectionのunique()との違い

Laravel には distinct() のほかに、Collection の unique() メソッドもあります。両者はシチュエーションによって使い分けが必要です。

比較項目 distinct() unique()
処理場所 データベース(SQL) PHP(アプリケーション)
対象 Query Builder / Eloquent Collection
パフォーマンス DBが大規模でも効率的 全データをPHPに読み込む
使いどころ DB取得時に重複を除外したい 取得済みコレクションを絞りたい
特定カラム指定 select()と組み合わせる キー名を引数に渡す

unique()の使い方

// コレクション取得後に重複排除
$users = User::all()->unique('email');

// クロージャで条件を指定
$users = User::all()->unique(function ($user) {
    return $user->first_name . $user->last_name;
});

推奨: 大量データを扱う場合は distinct() でDBレベルで絞り込んでから取得する方が効率的です。unique() は既に取得済みのコレクションに対して使いましょう。unique()の引数の指定方法や複数条件での使い方はunique — コレクションの重複要素を除去で詳しく解説しています。

groupBy()との違い

distinct()groupBy() はどちらも重複排除に使えますが、目的と動作が異なります。

比較項目 distinct() groupBy()
主な目的 重複行の除去 グループ集計
集計関数 使えない COUNT、SUM などが使える
複数カラム SELECT で指定した全カラム対象 指定カラムでグループ化
発行するSQL SELECT DISTINCT … … GROUP BY …
使いどころ 単純に重複を除いた一覧が欲しい 集計や件数が必要

groupBy()の使い方例

// 都市別のユーザー数を集計(groupByが適切)
$cityCounts = DB::table('users')
    ->select('city', DB::raw('COUNT(*) as user_count'))
    ->groupBy('city')
    ->get();

// 単純に重複なし一覧(distinctが適切)
$cities = DB::table('users')->select('city')->distinct()->get();

ポイント: 重複なしの一覧が欲しいだけなら distinct()、集計が必要なら groupBy() を選びましょう。groupBy()のキー指定やコールバックの使い方はgroupBy — キーやコールバックで要素をグループ化するを参考にしてください。

複数カラムで重複排除する場合の注意点

複数カラムで重複排除を行う場合、いくつかの注意点があります。

distinct()で複数カラムを指定する場合

// first_nameとlast_nameの組み合わせが重複する行を除外
$users = DB::table('users')
    ->select('first_name', 'last_name')
    ->distinct()
    ->get();

distinct()select() で指定した全カラムの組み合わせで評価します。上記の例では「first_name と last_name の組み合わせ」が重複する行が除外されます。

idなどの一意カラムをselectに含めない

よくある間違いとして、id のような一意カラムを select に含めてしまうケースがあります。

// NG: idが異なると重複として扱われないため意味がない
$users = DB::table('users')
    ->select('id', 'email')
    ->distinct()
    ->get();

// OK: 重複排除したいカラムのみ select する
$emails = DB::table('users')
    ->select('email')
    ->distinct()
    ->get();

MySQLのONLY_FULL_GROUP_BYに注意

MySQL の厳格モード(ONLY_FULL_GROUP_BY)が有効な場合、groupBy() を使った重複排除で、SELECT に含まれているが GROUP BY に含まれていないカラムはエラーになります。

// ONLY_FULL_GROUP_BY が有効な場合にエラーになる例
$users = User::select('first_name', 'last_name', 'email')
    ->groupBy('first_name', 'last_name')  // emailがGROUP BYにない
    ->get();

// 対策: groupByに含まれないカラムはRAW集計関数で包む
$users = User::select('first_name', 'last_name', DB::raw('MAX(email) as email'))
    ->groupBy('first_name', 'last_name')
    ->get();

パフォーマンスの最適化

大量データを扱う場合、distinct() の使い方によってパフォーマンスが大きく変わります。

インデックスの活用

distinct() を適用するカラムにインデックスを設定すると、クエリの実行速度が向上します。特に検索対象カラムへのインデックスは効果的です。

キャッシングの活用

頻繁に変更されないデータに対してはキャッシュを活用しましょう。

$uniqueEmails = Cache::remember('unique_emails', 3600, function () {
    return DB::table('users')->select('email')->distinct()->get();
});

Rawクエリの活用

複雑なクエリが必要な場合は Raw クエリも選択肢です。

$uniqueResults = DB::select(DB::raw("SELECT DISTINCT email FROM users WHERE active = 1"));

FAQ

Laravelで重複を除外するには?

データベースクエリなら distinct()、取得済みコレクションなら unique() を使います。

// DBクエリで重複排除
$emails = DB::table('users')->select('email')->distinct()->get();

// コレクションで重複排除
$emails = User::all()->unique('email');

distinctとgroupByの違いは?

distinct() は重複行を除いた一覧取得、groupBy() は集計(COUNT、SUM など)が目的です。単純な一覧取得なら distinct()、件数や合計などが必要なら groupBy() を使います。

Collectionでは何を使う?

Collection では unique() を使います。引数にカラム名かクロージャを渡すことで、特定のカラムを基準に重複を除去できます。

// カラム名で指定
$uniqueUsers = $users->unique('email');

// クロージャで指定
$uniqueUsers = $users->unique(fn($user) => $user->first_name . $user->last_name);

おわりに

Laravelでの重複排除は、状況に応じて使うメソッドが異なります。

  • Query Builder / Eloquent: distinct() — DBレベルで重複を除外(推奨)
  • Collection: unique() — PHPレベルで重複を除外
  • 集計が必要: groupBy() — GROUP BYでグループ化して集計

大量データを扱うケースでは、DBレベルで distinct() を使って取得データを絞り込むことがパフォーマンス上の鍵です。適切なメソッドを選択して、効率的なクエリを実装しましょう。

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする

コメント