LaravelのEloquentやクエリビルダを使って開発を進めていると、単純な検索だけでなく「特定条件に一致するレコードだけを抽出したい」という複雑な要件に出くわすことがあります。そんなときに役立つのが「サブクエリ」です。
本記事では、Laravelでサブクエリを用いた検索機能を実装する方法を、whereIn()とselectRaw()を軸に解説します。
サブクエリとは何か?
サブクエリ(副問い合わせ)とは、クエリの中に別のクエリを内包して条件に使うSQLの書き方です。LaravelではクエリビルダやEloquentを通じて、SQLを直書きせずにサブクエリを表現することができます。
たとえば「投稿数が5件以上あるユーザーの一覧」など、通常のリレーションでは書きにくい条件を扱うときに効果を発揮します。
whereInによるサブクエリの活用例
まずはwhereIn()を使って、投稿数が一定以上あるユーザーを取得する例を見てみましょう。
use Illuminate\Support\Facades\DB;
$users = DB::table('users')
->whereIn('id', function ($query) {
$query->select('user_id')
->from('posts')
->groupBy('user_id')
->havingRaw('COUNT(*) >= 5');
})
->get();
このコードでは、投稿数が5件以上のユーザーIDをpostsテーブルから取得し、それに該当するユーザーのみをusersテーブルから抽出しています。
whereInは配列も受け取れますが、このようにサブクエリを与えることで、より動的で強力な絞り込みが可能になります。
selectRawによるサブクエリの利用
次に、selectRaw()を使ってサブクエリの結果をカラムとして取得する例です。たとえば、各ユーザーの投稿数を一覧に含めたいとします。
$users = DB::table('users')
->select('users.*')
->selectRaw('(SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id) as post_count')
->get();
この例では、ユーザー情報とあわせて、投稿数(post_count)をサブクエリで算出して取得しています。
このようにselectRaw()は「集計値や計算値を追加したい」「表示用にサブクエリの値を取得したい」といったケースで便利です。
Eloquentでも同様に使える
クエリビルダだけでなく、Eloquentモデルを使っても同様のサブクエリが可能です。
whereIn + モデル
use App\Models\User;
$users = User::whereIn('id', function ($query) {
$query->select('user_id')
->from('posts')
->groupBy('user_id')
->havingRaw('COUNT(*) >= 5');
})->get();
selectRaw + モデル
$users = User::select('*')
->selectRaw('(SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id) as post_count')
->get();
Laravelのモデルを使えば、後続の処理やビューへの渡し方も自然で、読みやすいコードになります。
サブクエリを使うときの注意点
サブクエリは便利な反面、以下のような注意点もあります。
- パフォーマンスに注意:サブクエリ内でJOINやGROUP BYを多用すると重くなることがあるため、インデックス設計やキャッシュの併用を検討してください。
- Eloquentリレーションと併用が難しいケースあり:Eager Loading(with())などと併用する場合、整合性に注意が必要です。
- MySQL・PostgreSQLなどDBによって挙動差あり:サブクエリの挙動はDBごとに違いがあるので、環境に合わせてテストしましょう。
まとめ:Laravelでのサブクエリは柔軟な検索を可能にする強力な武器
Laravelでは、whereIn()やselectRaw()を使うことで、サブクエリを柔軟に活用できます。複雑な検索要件や動的なデータ集計を実現したいときには、積極的に活用してみましょう。
さらに発展的には、joinSub()やfromSub()といった方法もあり、ビューやランキング表示などにも応用が可能です。ニーズに応じて使い分けることで、Laravelの検索処理はよりパワフルなものになります。