SQLの確認では、組み立てた1クエリを見るのか、実行した一連のSQLを数えるのかを分けます。調査のためでもSQL全文やbindingsを公開せず、対象接続・記録範囲・停止方法を決めてから確認します。
値を記録しない観測メタデータの例
次の関数はQueryExecutedから接続名・SQL文字列のSHA256・実行時間だけを選び、SQL全文・bindingsを返しません。これだけでログへ登録されるわけではありません。必要な場合にアプリの登録箇所で呼び出し、専用チャンネル/期間/件数上限/閲覧権限を別途決めます。ハッシュも完全な匿名化ではなく、推測可能な入力を含む場合の取扱いが必要です。
<?php
namespace ContentExamples;
use Illuminate\Database\Events\QueryExecuted;
function querySummary(QueryExecuted $query): array
{
return [
'connection' => $query->connectionName,
'sql_sha256' => hash('sha256', $query->sql),
'time_ms' => $query->time,
];
}
Laravel 13と隔離MySQLで、バインド値とSQLへ直接書いたダミー秘密値の双方がこの関数の返却値に出ないことを確認しました。例外本文、他のロガー、保存済みファイルまで無害化する関数ではありません。
本数・時間を比較するときの手順
- 同じ検索語/ページ/接続とデータ条件で測る。メニューAjaxと記事表示など別HTTP処理のSQLを混ぜない。
- 一時的に収集する場合は、元の収集状態を確認し、対象処理だけを囲む。長時間workerのリスナー重複登録にも注意する。
- paginateの件数SQL、一覧、Eager Loading、ループ内取得を分ける。toSqlだけでは後続の追加SQLを数えられない。
- 本数だけでなく結果の集合・順序・総件数・時間を比較する。少ないSQLが必ず低負荷とは限らない。
- 収集を止め、既存の記録と新しいログ出力の両方を点検する。disableQueryLogでlistenまで止まったとは扱わない。
公式: QueryExecutedと接続/監視、Debugbarの現行配布先。パッケージは対象Laravel/PHPとの対応と閲覧制御を確認し、この記事の改善では本体に追加・有効化していません。関連はページングとQuery Builderです。
| この記事の要点 |
|
Laravel の Eloquent やクエリビルダが実際にどんな SQL を発行しているかを確認する方法を、目的別にまとめます。N+1 問題の発見や、意図しない条件で検索されていないかの確認に使えます。
目的別の方法の選び方
| やりたいこと | 方法 | 実行されるか |
|---|---|---|
| 特定の処理で発行された SQL を一覧で見る | DB::enableQueryLog / getQueryLog | 実行される |
| 1 つのクエリの SQL 文を確認する | toSql / toRawSql / dumpRawSql | 実行されない |
| すべての SQL をログファイルに残す | DB::listen | 実行される |
| ブラウザ上で常に確認する | Laravel Debugbar / Telescope | 実行される |
方法 1: DB::enableQueryLog と getQueryLog
次の旧例は画面へSQLと値を出すため、本番/公開画面で実行しないでください。 ログは接続ごとのメモリ収集で、必要な対象だけを短時間計測します。disableQueryLogは収集停止、flushQueryLogは収集済み配列の消去であり、保存済みログファイルの削除ではありません。DB::listenを解除する操作でもありません。
クエリログの収集を有効にしてから処理を実行し、最後に収集した結果を取り出します。
use Illuminate\Support\Facades\DB;
DB::enableQueryLog();
// ここで SQL を実行する処理
$items = TestTable::orderBy('col1', 'desc')->paginate(9);
dd(DB::getQueryLog());
次は旧出力の抜粋で、paginateの全SQLが1本という意味ではありません。 通常のpaginateは件数取得も行います。TestTableのモデル/スコープ/関連取得や版によって本数・テーブル名・出力は変わるため、以下を固定の期待値にしないでください。
array:1 [▼
0 => array:3 [▼
"query" => "select * from `test_table` order by `col1` desc limit 9 offset 0"
"bindings" => []
"time" => 2.22
]
]
query は SQL 文(プレースホルダは ? のまま)、bindings はプレースホルダに入る値、time は実行時間(ミリ秒)です。古い記事では use DB; と書かれていることがありますが、現在は use Illuminate\Support\Facades\DB; と完全なクラス名で書くのが確実です。
クエリログはメモリに溜まり続けるため、バッチ処理などで大量の SQL を流す場合は有効にしたままにしないでください。必要なら DB::disableQueryLog() や DB::flushQueryLog() で止める・消すことができます。別の DB 接続を使っている場合は DB::connection('mysql2')->enableQueryLog() のように接続ごとに有効にします。
方法 2: toSql / toRawSql で SQL 文だけ見る
クエリを実行せずに、組み立てられた SQL を確認できます。
$query = User::where('status', 'active')->where('age', '>=', 20);
// プレースホルダのままの SQL
echo $query->toSql();
// select * from `users` where `status` = ? and `age` >= ?
// バインド値を埋め込んだ SQL(Laravel 10.15 以降)
echo $query->toRawSql();
// select * from `users` where `status` = 'active' and `age` >= 20
// 出力して続行 / 出力して停止
$query->dumpRawSql();
$query->ddRawSql();
toRawSql()はバインド値を含む表示用SQLです。秘密値が入り得るため、そのまま共有・再実行しないでください。EXPLAINもDB/構文によって実行の有無や負荷が異なり、EXPLAIN ANALYZE等を確認目的で本番へ安易に実行しません。ただし、get() や first() を呼ぶ前のクエリビルダに対して使う必要があります。リレーションの Eager Loading(with)で追加発行される SQL は含まれないため、それらも見たい場合は方法 1 か方法 3 を使います。
方法 3: DB::listen ですべての SQL をログに出す
次はbindingsを保存してしまう旧例で、推奨設定ではありません。 APP_DEBUGだけを記録許可条件にせず、本番でtrueにして調査しないでください。生SQLに値を直接書いた場合は、bindingsを省略してもsql自体に秘密が残ります。下の全文ログ方式を採用せず、上のメタデータ例と必要な観測範囲を検討してください。
ログの出力先はチャンネル/日次分割/保存レベル等の設定に依存し、常にstorage/logs/laravel.logとは限りません。以下は旧リスナー例として、app/Providers/AppServiceProvider.php の boot() にリスナーを登録します。
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
public function boot(): void
{
if (config('app.debug')) {
DB::listen(function (QueryExecuted $query) {
Log::debug('SQL', [
'sql' => $query->sql,
'bindings' => $query->bindings,
'time_ms' => $query->time,
]);
});
}
}
旧例はAPP_DEBUG=trueを条件にしますが、機密値の除去や保持容量を保証しません。採用可否を決める際は専用のログチャンネル(config/logging.php に sql チャンネルを追加し、Log::channel('sql') で書く)を作ると、通常のログと混ざらず見やすくなります。
遅いクエリだけを検出したい場合は、DB::whenQueryingForLongerThan() を使うと、1 リクエスト内のクエリ合計時間がしきい値を超えたときだけ処理を実行できます。
方法 4: 開発用パッケージで画面に表示する
- Laravel Debugbar(現行公式の配布名はfruitcake/laravel-debugbar。旧barryvdh名と区別): ページ下部にツールバーを出し、そのリクエストで実行された SQL・件数・時間・重複クエリを表示する
- Laravel Telescope(公式): リクエスト・クエリ・ジョブ・例外などを記録し、専用画面で後から閲覧できる。遅いクエリの強調表示もある
Debugbarは開発用として扱います。Telescopeはローカル限定導入と、本番で認可・収集対象・保持/削除を設計する導入を区別します。--devだけで閲覧制御が完成するわけではありません。公式Telescopeの認可とフィルターを確認し、導入用のDB変更も含め通常の承認手順へ分離してください。
よくある落とし穴
- getQueryLog が空になる: enableQueryLog を SQL 実行より後に呼んでいる、または別の接続で実行されている
- dd() の後の処理が動かない: dd は処理を止める。続行したいなら
dump()を使う - 本番でログが肥大化する: DB::listen を無条件に登録すると、全リクエストの SQL が記録されディスクを圧迫する。パスワードや個人情報がバインド値としてログに残る点にも注意
- toRawSql の値は参考用: 埋め込み表示はあくまで確認用。実際の実行はプリペアドステートメントで行われている
確認方法
方法 3 を設定した場合は、ページを 1 回開いてから storage/logs/laravel.log を確認します。同じ形のSQLの繰返しは、ループ・意図した繰返し・再試行等を呼出箇所と照合します。N+1ならEloquentのwithやQuery Builderの一括取得を検討しますが、withだけであらゆる重複を除けるわけではありません。修正前後でログに出る SQL の件数を比べると、効果を確認できます。
関連
- PHPログ出力完全ガイド(error_log/Monolog/PSR-3/Laravel/logrotate)
- PHP var_dump をログ出力する完全ガイド
- PHP エラーログ出力 (error_log / Monolog / Laravel Log) 完全ガイド
- Django で SQL ログを出力する方法|django.db.backends と N+1 の発見
子ページはありません
- インストールと設定
- クイックスタート & チュートリアル(初心者向け)
- クイックスタート & チュートリアル(中級者向け)
- ルーティング
- Bladeテンプレート(ビュー/レイアウト)
- コントローラー
- マイグレーションとテーブル定義
- データベースの設定
- Eloquentモデル (ORM)
- SQLとクエリビルダー
- バリデーション
- .envファイルの設定値へのアクセス
- 動作環境による分岐処理
- configフォルダ配下の設定値へのアクセス
- assetヘルパーを利用したpublicフォルダへのアクセス
- storageフォルダへのアクセス
- アプリケーション名の変更
- メンテナンス
- ログイン画面(認証システム)の作成
- ログインの必須化
- ログインユーザー情報の取得
- ルートの認証化
- 本番サーバーへのデプロイ方法
- 多言語化
- csrf_field
- ファイルのダウンロード
- CSVのアップロードおよび読み込み(maatwebsite/excel)
- ページタイトルの設定
- コマンド一覧
- エラー一覧
- SQLの実行ログ出力方法
- キャッシュのクリア
- Selectの結果の最初もしくは最後に任意の値を追加する方法
- ajaxでPOST通信する際の注意点
- ソーシャルログインの実装
- セッション情報の確認
- ログイン、ユーザー登録、パスワードリセット後のリダイレクト先の変更方法
- redirectやreturn viewにメッセージを付与する方法
- クッキー(cookie)の設定と取得
- クラスの再読み込み
- csrfの有効時間を変更する方法
- ViewComposerを用いてviewに共通の値を付与する方法
- View::shareを用いて共通の値を各ビューに渡す方法
- ミドルウェアを用いた処理の共通化
- Middleware内でAuth::check()などを使用する方法
- Controller以外でリダイレクトする方法
- セッションの値の取得/保存/更新/削除
- $requestの値を変更する方法
- 常時SSL化
- ページング(ページネーション)をする方法
- vue.jsとの連携
- Vue.jsと連携するSPA実行環境構築
- .envの値をvue.jsで参照する方法
- vue.jsを本番環境にリリースする方法
- could not find driver(Windows, MySQL編)
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスのソフト一覧|Blender・Metasequoiaと用途別の選び方
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Visual Studio「デザイナーを表示できません」エラーの原因と対処 2026-10-28 12:06:00
- SSH 認証に失敗しました。再試行してください の対処 2026-10-28 12:06:00
- SSH2 秘密鍵の読み込みに失敗しました の原因と対処 2026-10-28 12:06:00
- TeraTerm のよくあるエラーと対処法完全ガイド — Connection refused / 2026-10-28 12:06:00
- Visual Studio エラー一覧|コンパイルエラー・警告の確認方法と対処 2026-10-28 11:39:57
- Whitelabel Error Page の原因と消し方|Spring Boot のエラーページ 2026-10-28 11:39:57
- Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法 2026-10-28 11:39:57
- SQL WHERE 句完全ガイド 2026-10-28 11:39:57
- SQL JOIN 完全ガイド (INNER/LEFT/RIGHT/FULL OUTER) 2026-10-28 11:39:57
- tomcat の起動 / 停止ログと catalina.log・catalina.out の違い 2026-10-28 10:24:23
- Tomcat の起動確認方法 5 つ|localhost:8080・ポート・プロセス・ログ 2026-10-28 10:24:23
- Oracle 19c COMMITの使い方: 暗黙コミット・自動確定・応答不明時の確認 2026-10-28 09:59:09
- Oracle 19c DELETE文|削除対象の確認・COMMIT前の判断・ROLLBACKの条件 2026-10-28 09:59:09
- Oracle 19c INSERT文|INSERT ALL・SELECT・RETURNINGとMERGEの使い分け 2026-10-28 09:59:09
- Oracle 19cのトランザクション制御|COMMIT・ROLLBACK・分離レベルと接続設定 2026-10-28 09:59:09