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() を使って取得データを絞り込むことがパフォーマンス上の鍵です。適切なメソッドを選択して、効率的なクエリを実装しましょう。

コメント