タイトル: SELECT
SEOタイトル: Laravel Query Builder の SELECT 完全ガイド
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li>基本: <code>DB::table('users')->select('name', 'email')->get()</code></li><li>生 SQL は <code>selectRaw()</code>、重複排除は <code>distinct()</code>、列追加は <code>addSelect()</code></li><li>取得は <code>get() / first() / find() / value() / pluck()</code> を場面で使い分け</li><li>JOIN / GROUP BY / HAVING / サブクエリ <code>selectSub()</code> もチェーンで書ける</li><li>Eloquent でも同じ感覚で <code>User::select('name')->where(...)->get()</code></li></ul></td></tr></table>
<h2>基本: select() で列を指定</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">use Illuminate\Support\Facades\DB;
// 全列
$users = DB::table('users')->get();
// 列指定
$users = DB::table('users')->select('id', 'name', 'email')->get();
// エイリアス
$users = DB::table('users')
->select('id', 'name AS user_name', 'email')
->get();
// 配列で渡す
$users = DB::table('users')->select(['id', 'name'])->get();</code></pre>
<h2>selectRaw: 生 SQL 式</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// COUNT / SUM 等の集計
$stats = DB::table('orders')
->selectRaw('user_id, COUNT(*) AS cnt, SUM(amount) AS total')
->groupBy('user_id')
->get();
// 日付フォーマット
$logs = DB::table('access_logs')
->selectRaw("DATE_FORMAT(created_at, '%Y-%m-%d') AS day, COUNT(*) AS hits")
->groupBy('day')
->get();
// バインドパラメータを使う(SQL インジェクション対策)
$users = DB::table('users')
->selectRaw('price * ? AS price_with_tax', [1.1])
->get();</code></pre>
<h2>addSelect: 列を追加</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">$query = DB::table('users')->select('id', 'name');
if ($request->include_email) {
$query->addSelect('email');
}
if ($request->include_role) {
$query->addSelect('role');
}
$users = $query->get();</code></pre>
<h2>distinct: 重複排除</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// ユニークな国一覧
$countries = DB::table('users')
->select('country')
->distinct()
->get();
// 複数列の組み合わせで distinct
$pairs = DB::table('orders')
->select('user_id', 'product_id')
->distinct()
->get();</code></pre>
<h2>WHERE / ORDER BY / GROUP BY / HAVING</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">$users = DB::table('users')
->select('id', 'name', 'created_at')
->where('active', 1)
->where('age', '>=', 18)
->whereIn('country', ['JP', 'US', 'KR'])
->whereNull('deleted_at')
->whereBetween('age', [20, 65])
->orderBy('created_at', 'desc')
->get();
// GROUP BY / HAVING
$summary = DB::table('orders')
->select('user_id', DB::raw('SUM(amount) AS total'))
->groupBy('user_id')
->having('total', '>', 10000)
->orderByDesc('total')
->get();</code></pre>
<h2>JOIN を組み合わせた SELECT</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">$rows = DB::table('users')
->join('orders', 'users.id', '=', 'orders.user_id')
->leftJoin('payments', 'orders.id', '=', 'payments.order_id')
->select(
'users.id',
'users.name',
'orders.id AS order_id',
'orders.amount',
'payments.paid_at'
)
->where('orders.status', 'completed')
->orderBy('orders.created_at', 'desc')
->get();</code></pre>
<h2>サブクエリ: selectSub / whereExists</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// selectSub: 各 user に最終ログイン時刻を付与
$users = DB::table('users')
->select('id', 'name')
->selectSub(function ($q) {
$q->select('created_at')
->from('login_logs')
->whereColumn('login_logs.user_id', 'users.id')
->orderByDesc('created_at')
->limit(1);
}, 'last_login_at')
->get();
// whereExists: 注文を持つユーザー
$users = DB::table('users')
->whereExists(function ($q) {
$q->select(DB::raw(1))
->from('orders')
->whereColumn('orders.user_id', 'users.id');
})
->get();</code></pre>
<h2>取得メソッドの使い分け</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>メソッド</th><th>返り値</th><th>用途</th></tr></thead>
<tbody>
<tr><td><code>get()</code></td><td>Collection of stdClass</td><td>複数行取得</td></tr>
<tr><td><code>first()</code></td><td>stdClass / null</td><td>1 行目を取得</td></tr>
<tr><td><code>find($id)</code></td><td>stdClass / null</td><td>PK で 1 件</td></tr>
<tr><td><code>value('column')</code></td><td>scalar</td><td>1 列だけ取得</td></tr>
<tr><td><code>pluck('name')</code></td><td>Collection<string></td><td>単一列をリスト化</td></tr>
<tr><td><code>pluck('name', 'id')</code></td><td>Collection<id => name></td><td>キー付きリスト</td></tr>
<tr><td><code>count()</code></td><td>int</td><td>行数</td></tr>
<tr><td><code>exists()</code></td><td>bool</td><td>存在チェック(高速)</td></tr>
<tr><td><code>chunk(100, fn)</code></td><td>void</td><td>大量データ分割処理</td></tr>
<tr><td><code>cursor()</code></td><td>Generator</td><td>1 行ずつメモリ節約</td></tr>
</tbody>
</table>
<h2>pluck の便利な使い方</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">// id だけのリスト
$ids = DB::table('users')->where('active', 1)->pluck('id')->all();
// [1, 2, 3, 5, 8]
// id => name の連想配列(HTML select 用)
$options = DB::table('users')->pluck('name', 'id');
// [1 => 'Taro', 2 => 'Hanako', ...]
// Blade で
// <select>
// @foreach ($options as $id => $name)
// <option value="{{ $id }}">{{ $name }}</option>
// @endforeach
// </select></code></pre>
<h2>ページネーション</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">$users = DB::table('users')
->select('id', 'name')
->orderBy('id')
->paginate(20); // 1ページ20件
// Blade
// {{ $users->links() }}
// simplePaginate は総件数を計算せず軽量
$users = DB::table('users')->simplePaginate(20);
// カーソルベース(大量データ向け)
$users = DB::table('users')->orderBy('id')->cursorPaginate(20);</code></pre>
<h2>Eloquent でも同じ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-php">use App\Models\User;
// 全列
$users = User::all();
// 列指定
$users = User::select('id', 'name', 'email')->where('active', 1)->get();
// リレーションも eager load しつつ
$users = User::with('orders:id,user_id,amount')
->select('id', 'name')
->get();
// pluck
$emails = User::where('active', 1)->pluck('email');</code></pre>
<h2>パフォーマンスのコツ</h2>
<ul>
<li><strong>必要な列だけ select</strong>: TEXT / JSON 列を含む全列取得は重い</li>
<li><strong>EXPLAIN で確認</strong>: <code>DB::table(...)->toSql()</code> でクエリ確認 → MySQL で EXPLAIN</li>
<li><strong>N+1 回避</strong>: Eloquent では <code>with()</code> で eager load</li>
<li><strong>chunk / cursor</strong>: 10 万行以上扱うときは <code>chunk(1000)</code> or <code>cursor()</code></li>
<li><strong>インデックス</strong>: where / orderBy 列に index、複合 index は左端から</li>
<li><strong>QueryBuilder の <code>toSql()</code> + DB::listen() でロガー</strong></li>
</ul>
<h2>FAQ</h2>
<p><strong>Q: get() と first() の違い</strong><br />
A: get() は Collection(複数行)、first() は最初の 1 行のオブジェクト or null。「絶対1件しかないクエリ」でも get() を呼ぶと配列処理が必要になり面倒。</p>
<p><strong>Q: 実行された SQL を確認したい</strong><br />
A: <code>DB::table(...)->toSql()</code> でクエリ文字列、<code>DB::table(...)->dd()</code> でバインド込みダンプ。<code>DB::listen(fn($q) => Log::info($q->sql))</code> で全クエリログ。</p>
<p><strong>Q: select で alias を付けると Eloquent で取れない</strong><br />
A: モデル属性として取得するには <code>$user->getAttribute('alias')</code> or <code>$user->alias</code>。生クエリの結果は stdClass なので注意。</p>