クエリビルダーで、生のSQLクエリを文字列として出力するにはどうすればよいですか?


544

次のコードがあるとします:

DB::table('users')->get();

上記のデータベースクエリビルダーが生成する生のSQLクエリ文字列を取得します。この例では、になりますSELECT * FROM users

どうすればよいですか?


14
Laravel Eloquent ORMが生のクエリを取得:echo User::where('status', 1)->toSql();
Muhammad Shahzad 2017年

私はLaravel-Telescopeのパケットを使用しています。それはすべてのクエリをログに記録し、さらに多くのことを行います。
ビンサ

回答:


660

最後に実行したクエリを画面に出力するには、次のように使用できます。

DB::enableQueryLog(); // Enable query log

// Your Eloquent query executed by using get()

dd(DB::getQueryLog()); // Show results of log

最新のクエリは配列の一番下になると思います。

あなたはそのようなものを持つでしょう:

array(1) {
  [0]=>
  array(3) {
    ["query"]=>
    string(21) "select * from "users""
    ["bindings"]=>
    array(0) {
    }
    ["time"]=>
    string(4) "0.92"
  }
}

(下のジョシュアのコメントに感謝します。)


2
うーんイムないでくださいしかし、あなたが作曲パッケージであなたが望むものをaccompishすることができるかもしれstackoverflow.com/a/17339752/813181
jfortunato

9
でも使用して、アプリケーションのログにそれを出力する方がよいかもしれませんLogクラス: Log::debug(DB::getQueryLog())
msturdy

35
これはデフォルトで現在オフになっているため、有効にする必要がある場合があります。あなたは一時的にそれをオンにするには、このコマンドを使用することができます:DB::enableQueryLog();
ジョシュアフリッケ

5
あなたの答えを試してみました。私が試したことはあるDB::enableQueryLog(); dd(DB::getQueryLog());。しかし、それだけで返します[]....
私はほとんど愚かな人です

6
複数のデータベースがある場合は、必要になることがありますDB::connection('database')->getQueryLog()
DamienÓCeallaigh 2018

745

インスタンスでtoSql()メソッドを使用しQueryBuilderます。

DB::table('users')->toSql() 戻ります:

「ユーザー」から*を選択します

これは、イベントリスナーを接続するよりも簡単です。また、構築中の任意の時点で実際にクエリがどのように表示されるかを確認することもできます。


6
Laravel外でEloquentを使用する場合、これが最も簡単な方法だと思います
Gab

8
@Stormssonこれは不可能です。PHPでは、バインディングがその値に置き換えられたクエリが存在しないためです。クエリ全体を取得するには、MySQLからクエリを記録する必要があります。:ここに詳細がありますstackoverflow.com/questions/1786322/...
マシュー・

40
@Stormssonメソッドを使用できますgetBindings。これは、SQLステートメントにバインドされるためにバインドを返します。
danronmoon

2
Eloquentがクエリログに表示されないため実行を拒否する複雑なクエリのデバッグに非常に役立ちます。
BobChao87 2016年

34
bindindsでクエリを取得するには$query = \DB::table('users')->where('id', 10); $sql = str_replace_array('?', $query->getBindings(), $query->toSql()); dd($sql);
Ennio Sousa

88

DB::QueryLog()クエリを実行した後にのみ機能します$builder->get()。クエリを実行する前にクエリを取得したい場合は、$builder->toSql()メソッドを使用できます。これは、SQLを取得してバインドする方法の例です。

    $query = str_replace(array('?'), array('\'%s\''), $builder->toSql());
    $query = vsprintf($query, $builder->getBindings());
    dump($query);

    $result = $builder->get();

または、存在しないテーブルまたは列を呼び出すようなクエリエラーを発生させるだけで、生成されたクエリが例外XDに表示されます


3
これは、断然、最善の答えであり、単純で真っ直ぐです。ありがとう:)
Sobakus

18
ワンライナーとして:$query = vsprintf(str_replace(array('?'), array('\'%s\''), $builder->toSql()), $builder->getBindings());
kramer65

これは、ネイティブ関数としての枠組みに含まれるべきである。..ありがとう
トマーシュMleziva

クエリにパーセント記号が既に含まれているLIKE場合や、日付をフォーマットする場合、これは機能しないことに注意してください。二重パーセント記号で最初にそれらをエスケープする必要があります。
不明な開発

これを行うときにセキュリティ上の懸念はありますか?バインディングはサニタイズされ$builder->getBindings()ますか?
ソリダウ

56

'illuminate.query'イベントをリッスンできます。クエリの前に、次のイベントリスナーを追加します。

Event::listen('illuminate.query', function($query, $params, $time, $conn) 
{ 
    dd(array($query, $params, $time, $conn));
});

DB::table('users')->get();

これは次のようなものを出力します:

array(4) {
  [0]=>
  string(21) "select * from "users""
  [1]=>
  array(0) {
  }
  [2]=>
  string(4) "0.94"
  [3]=>
  string(6) "sqlite"
}

1
Laravel 4で未定義のメソッドIlluminate \ Database \ Query \ Builder :: listen()への呼び出しが発生します
Miguel Stevens

2
ありがとうございます。ddは指定された変数をダンプしてスクリプトの実行を終了する関数であり、イベントをインポートする関数であることに注意してくださいuse Illuminate\Support\Facades\Event;
radtek

1
@radtek:ファサードなので、代わりにuse Illuminate\Support\Facades\Event;単純に行うことができますuse Event;
TachyonVortex

50

Laravelを使用せずにIlluminateを使用してログを取得しようとしている場合:

\Illuminate\Database\Capsule\Manager::getQueryLog();

次のようなクイック関数をノックアップすることもできます:

function logger() {
    $queries = \Illuminate\Database\Capsule\Manager::getQueryLog();
    $formattedQueries = [];
    foreach( $queries as $query ) :
        $prep = $query['query'];
        foreach( $query['bindings'] as $binding ) :
            $prep = preg_replace("#\?#", is_numeric($binding) ? $binding : "'" . $binding . "'", $prep, 1);
        endforeach;
        $formattedQueries[] = $prep;
    endforeach;
    return $formattedQueries;
}

編集

更新されたバージョンでは、クエリロギングがデフォルトで無効になっているようです(上記は空の配列を返します)。オンに戻すには、カプセルマネージャを初期化するときに、接続のインスタンスを取得してenableQueryLogメソッドを呼び出します

$capsule::connection()->enableQueryLog();

もう一度編集

実際の質問を考慮に入れると、以前のすべてのクエリではなく、現在の単一のクエリを変換するために次のことを実際に行うことができます。

$sql = $query->toSql();
$bindings = $query->getBindings();

クエリ "name = [{" name ":" rifat "}]"からこのタイプの戻り値を取得しています。 "name = rifat"のみを取得するために何をする必要がありますか?
法人

バインディングを出力します。文字列ではなく配列を渡しているようです
Luke Snowden

これは便利なスタートですが、たとえばのような文字列を渡す場合など、パラメーター化された値の前後に単一引用符を追加することを怠っているよう'US/Eastern'です。
ライアン

1
@ライアンこれは本当なので、私が述べquick functionた理由。基礎となるコードはprepare(php.net/manual/en/mysqli.prepare.php)メソッドを使用すると思い?ます。そのため、だけが必要です。php.net/manual/en/function.is-numeric.phpを使用して、入力を単一引用符でカプセル化するかどうかを決定できます。
ルークスノーデン2017年

1
@LukeSnowdenあなたの答えは天才です!私はようやく時間をかけて新しいバージョン(あなたのis_numericアイデアを含めるために上で編集したもの)を試してみました。これ大好き。ありがとうございました。
ライアン

36

クエリ文字列を取得するための雄弁なメソッドがあります。

toSql()

私たちの場合には、

 DB::table('users')->toSql(); 

返す

select * from users

SQLクエリ文字列を返す正確なソリューションです。これが役に立てば幸いです...


11
クエリバインディングについてはどうですか?たとえば->where('foo', '=', 'bar')、SQLでバーが表示されない場合
Toskan


24

laravel 5.1とMySQLを使用している場合は、私が作成したこの関数を使用できます。

/*
 *  returns SQL with values in it
 */
function getSql($model)
{
    $replace = function ($sql, $bindings)
    {
        $needle = '?';
        foreach ($bindings as $replace){
            $pos = strpos($sql, $needle);
            if ($pos !== false) {
                if (gettype($replace) === "string") {
                     $replace = ' "'.addslashes($replace).'" ';
                }
                $sql = substr_replace($sql, $replace, $pos, strlen($needle));
            }
        }
        return $sql;
    };
    $sql = $replace($model->toSql(), $model->getBindings());

    return $sql;
}

入力パラメーターとして、次のいずれかを使用できます

Illuminate \ Database \ Eloquent \ Builder

Illuminate \ Database \ Eloquent \ Relations \ HasMany

Illuminate \ Database \ Query \ Builder


回答が改善され、コメントに加えられたすべてのコメントが含まれるようになりました。どうもありがとうございました。
Yevgeniy Afanasyev 2017

13

まず、次を呼び出してクエリログを有効にする必要があります。

DB::enableQueryLog();

DBファサードを使用してクエリを実行すると、次のように記述できます。

dd(DB::getQueryLog());

出力は以下のようになります。

array:1 [▼
  0 => array:3 [▼
    "query" => "select * from `users` left join `website_user` on `users`.`id` = `website_user`.`user_id` left join `region_user` on `users`.`id` = `region_user`.`user_id` left ▶"
    "bindings" => array:5 [▶]
    "time" => 3.79
  ]
]

非常に役立つ回答
Anoop PS

こんにちは、私は$ result = DB :: select( 'select * from sqrt_user_modules where user_id =:id'、['id' => $ user]);を使用しました。DB :: enableQueryLog(); しかし、出力を取得しませんでしたdd(DB :: getQueryLog());
Anoop PS

ライブラリを含める必要がありますか
Anoop PS

1
ステップ1:DB :: enableQueryLog(); ステップ2:$ result = DB :: select( 'select * from sqrt_user_modules where user_id =:id'、['id' => $ user]); ステップ3:dd(DB :: getQueryLog());
Ravi Mane

12

これも議論されてきましたが、雄弁な最後のクエリまたは最後のクエリをデバッグするために私が提案できる最も優れたソリューションです。

// query builder
$query = DB::table('table_name')->where('id', 1);

// binding replaced
$sql = str_replace_array('?', $query->getBindings(), $query->toSql());

// for laravel 5.8^
$sql = Str::replaceArray('?', $query->getBindings(), $query->toSql());

// print
dd($sql);

10

最初の方法:

toSql()メソッドを使用して次のことを簡単に行うことができます、

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

echo $query->toSql();

それが機能しない場合は、laravelのドキュメントから設定できます

2番目の方法:

それを行う別の方法は

DB::getQueryLog()

しかし、それが空の配列を返す場合、デフォルトでは無効になっています

有効にするだけでDB::enableQueryLog()動作します:)

詳細については、Github Issueにアクセスして詳細を確認してください。

それが役に立てば幸い :)


10

「macroable」バインディングを使用してSQLクエリを取得するための交換。

  1. メソッドに以下のマクロ関数を追加します。AppServiceProvider boot()

    \Illuminate\Database\Query\Builder::macro('toRawSql', function(){
        return array_reduce($this->getBindings(), function($sql, $binding){
            return preg_replace('/\?/', is_numeric($binding) ? $binding : "'".$binding."'" , $sql, 1);
        }, $this->toSql());
    });
  2. Eloquent Builderのエイリアスを追加します。(Laravel 5.4以降

    \Illuminate\Database\Eloquent\Builder::macro('toRawSql', function(){
        return ($this->getQuery()->toRawSql());
    });
  3. 次に、通常どおりデバッグします。(Laravel 5.4以降

    例えばクエリビルダー

    \Log::debug(\DB::table('users')->limit(1)->toRawSql())

    例:Eloquent Builder

    \Log::debug(\App\User::limit(1)->toRawSql());

注:Laravel 5.1から5.3まで、Eloquent BuilderはMacroableトレイトを利用しないためtoRawSql、Eloquent Builderにその場でエイリアスを追加できません。同じことを達成するには、以下の例に従ってください。

例:Eloquent BuilderLaravel 5.1-5.3

\Log::debug(\App\User::limit(1)->getQuery()->toRawSql());

おっと、私は遅れて来ました。マクロを使用して回答を送信したいだけです。これが最も良い答えです。承認された回答である必要があります:D
nmfzone '29年

ベースモデルのスコープで後者を抽象化できます
Ogier Schelvis '13 / 09/13

8

デバッグバーパッケージを使用する

composer require "barryvdh/laravel-debugbar": "2.3.*"

ここに画像の説明を入力してください


7

laravel 5.2以降。DB::listen実行されたクエリを取得するために使用できます。

DB::listen(function ($query) {
    // $query->sql
    // $query->bindings
    // $query->time
});

または、単一のBuilderインスタンスをデバッグする場合は、toSqlメソッドを使用できます。

DB::table('posts')->toSql(); 

1
listenは役に立ち、クエリを実行する前に宣言し、メソッド内のSQLとバインディングをダンプします。不完全ですが、他の回答よりも速く/簡単に機能します。
Andrew

7

最も簡単な方法は、意図的な間違いを犯すことです。たとえば、次の関係の完全なSQLクエリを確認したいとします。

 public function jobs()
        {
            return $this->belongsToMany(Job::class, 'eqtype_jobs')
                   ->withPivot(['created_at','updated_at','id'])
                   ->orderBy('pivot_created_at','desc');
        }

列が見つからないようにするためだけに、ここで選択しcreated_atcreated_ats末尾sに次のように追加して変更しました。

public function jobs()
            {
                return $this->belongsToMany(Job::class, 'eqtype_jobs')
                       ->withPivot(['created_ats','updated_at','id'])
                       ->orderBy('pivot_created_at','desc');
            }

したがって、デバッガーは次のエラーを返します。

(4/4)ErrorException SQLSTATE [42S22]:列が見つかりません: 'フィールドリスト'で1054不明な列'eqtype_jobs.created_ats'(SQL:選択 jobs*、。 eqtype_jobsset_idとしてpivot_set_ideqtype_jobsjob_idなどpivot_job_ideqtype_jobscreated_ats などpivot_created_atseqtype_jobsupdated_atなど pivot_updated_ateqtype_jobsidなどpivot_idからjobs内部ジョインeqtype_jobsjobsid= eqtype_jobsjob_idここ eqtype_jobsset_id= 56によって順pivot_created_atDESC制限20)がオフセット0(表示:/home/said/www/factory/resources/views/set/show.blade.php)

上記のエラーメッセージは、間違いのある完全なSQLクエリを返します

SQL: select  jobs.*, eqtype_jobs.set_id as pivot_set_id,  eqtype_jobs.job_id as pivot_job_id, eqtype_jobs.created_ats as pivot_created_ats, eqtype_jobs.updated_at as  pivot_updated_at, eqtype_jobs.id as pivot_id from jobs inner join eqtype_jobs on jobs.id = eqtype_jobs.job_id where  eqtype_jobs.set_id = 56 order by pivot_created_at desc limit 20 offset 0

今度は、screated_atから余分なものを削除し、phpMyAdmin SQLエディターなどのSQLエディターで好きなようにこのSQLをテストします。

通知:

ソリューションはLaravel 5.4でテストされています。


2
これは断然最高の答えです!とても簡単!:)
Picard

これはバインディングを含むクエリを表示しません。つまり、バインディングは次のように表示されます:id
Shantha Kumara

@ShanthaKumara確かに、あなたが使用したLaravelのバージョンや構成は何なのかわかりません。ただし、私の回答のすべてのスニペットまたはコードは、Laravel 5.4プロジェクトの実際のコード出力からコピーして貼り付けました。
SaidbakR


6

Laravel 5.8.15以降、クエリビルダー dddumpメソッド追加され、実行できるようになりました

DB::table('data')->where('a', 1)->dump();

ありがとう。ddは本当にうまくいきます。DB :: table( 'data')-> where( 'a'、1)-> dd();
Waqas

リストされている他の回答よりも優れています。
Hamees A. Khan

5

これは関数で、基本モデルクラスに配置しました。クエリビルダーオブジェクトを渡すだけで、SQL文字列が返されます。

function getSQL($builder) {
  $sql = $builder->toSql();
  foreach ( $builder->getBindings() as $binding ) {
    $value = is_numeric($binding) ? $binding : "'".$binding."'";
    $sql = preg_replace('/\?/', $value, $sql, 1);
  }
  return $sql;
}


4

laravel 5.5.Xの場合

アプリケーションによって実行される各SQLクエリを受け取りたい場合は、listenメソッドを使用できます。このメソッドは、クエリのロギングやデバッグに役立ちます。クエリリスナーをサービスプロバイダーに登録できます。

<?php

namespace App\Providers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        DB::listen(function ($query) {
            // $query->sql
            // $query->bindings
            // $query->time
        });
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

ソース


4

この関数をアプリケーションに追加して、呼び出すだけです。

function getQuery($sql){
        $query = str_replace(array('?'), array('\'%s\''), $sql->toSql());
        $query = vsprintf($query, $sql->getBindings());     
        return $query;
}

出力: "select * from userwhere lang= 'en' and status= '1' order by updated_atdesc limit 25 offset 0"


3

このパッケージを使用して、ページをロードするときに実行されているすべてのクエリを取得できます

https://github.com/barryvdh/laravel-debugbar

このパッケージは、クエリエラーがない場合に適しています。SQLエラーがある場合、何も表示されません
lewis4u


2

Laravelを使用していないがEloquentパッケージを使用している場合:

use \Illuminate\Database\Capsule\Manager as Capsule;
use \Illuminate\Events\Dispatcher;
use \Illuminate\Container\Container;

$capsule = new Capsule;

$capsule->addConnection([
    // connection details
]);
// Set the event dispatcher used by Eloquent models... (optional)
$capsule->setEventDispatcher(new Dispatcher(new Container));

// Make this Capsule instance available globally via static methods... (optional)
$capsule->setAsGlobal();

// Setup the Eloquent ORM...(optional unless you've used setEventDispatcher())
$capsule->bootEloquent();

// Listen for Query Events for Debug
$events = new Dispatcher;
$events->listen('illuminate.query', function($query, $bindings, $time, $name)
{
    // Format binding data for sql insertion
    foreach ($bindings as $i => $binding) {
        if ($binding instanceof \DateTime) {
            $bindings[$i] = $binding->format('\'Y-m-d H:i:s\'');
        } else if (is_string($binding)) {
            $bindings[$i] = "'$binding'";`enter code here`
        }
    }

    // Insert bindings into query
    $query = str_replace(array('%', '?'), array('%%', '%s'), $query);
    $query = vsprintf($query, $bindings);

    // Debug SQL queries
    echo 'SQL: [' . $query . ']';
});

$capsule->setEventDispatcher($events);

2

あなたは時計仕掛けを使うことができます

ClockworkはPHP開発用のChrome拡張機能であり、開発者ツールを新しいパネルで拡張し、リクエスト、ヘッダー、データの取得と送信、Cookie、セッションデータ、データベースクエリに関する情報を含む、PHPアプリケーションのデバッグとプロファイリングに役立つあらゆる種類の情報を提供します。ルート、アプリケーションランタイムの視覚化など。

Firefoxでも動作します


2

いくつかのクエリからSQLとバインディングを取得する簡単な関数をいくつか作成しました。

/**
 * getSql
 *
 * Usage:
 * getSql( DB::table("users") )
 * 
 * Get the current SQL and bindings
 * 
 * @param  mixed  $query  Relation / Eloquent Builder / Query Builder
 * @return array          Array with sql and bindings or else false
 */
function getSql($query)
{
    if( $query instanceof Illuminate\Database\Eloquent\Relations\Relation )
    {
        $query = $query->getBaseQuery();
    }

    if( $query instanceof Illuminate\Database\Eloquent\Builder )
    {
        $query = $query->getQuery();
    }

    if( $query instanceof Illuminate\Database\Query\Builder )
    {
        return [ 'query' => $query->toSql(), 'bindings' => $query->getBindings() ];
    }

    return false;
}

/**
 * logQuery
 *
 * Get the SQL from a query in a closure
 *
 * Usage:
 * logQueries(function() {
 *     return User::first()->applications;
 * });
 * 
 * @param  closure $callback              function to call some queries in
 * @return Illuminate\Support\Collection  Collection of queries
 */
function logQueries(closure $callback) 
{
    // check if query logging is enabled
    $logging = DB::logging();

    // Get number of queries
    $numberOfQueries = count(DB::getQueryLog());

    // if logging not enabled, temporarily enable it
    if( !$logging ) DB::enableQueryLog();

    $query = $callback();

    $lastQuery = getSql($query);

    // Get querylog
    $queries = new Illuminate\Support\Collection( DB::getQueryLog() );

    // calculate the number of queries done in callback
    $queryCount = $queries->count() - $numberOfQueries;

    // Get last queries
    $lastQueries = $queries->take(-$queryCount);

    // disable query logging
    if( !$logging ) DB::disableQueryLog();

    // if callback returns a builder object, return the sql and bindings of it
    if( $lastQuery )
    {
        $lastQueries->push($lastQuery);
    }

    return $lastQueries;
}

使用法:

getSql( DB::table('users') );
// returns 
// [
//     "sql" => "select * from `users`",
//     "bindings" => [],
// ]

getSql( $project->rooms() );
// returns
// [
//     "sql" => "select * from `rooms` where `rooms`.`project_id` = ? and `rooms`.`project_id` is not null",
//     "bindings" => [ 7 ],
// ]

2

私はこのフレームワークを愛しているのと同じくらい、それががらくたのように振る舞うとき私は嫌いです。

DB::enableQueryLog()まったく役に立たない。DB::listen同様に役に立たない。私が言ったときにクエリの一部が表示されました$query->count()$query->get()、表示しても何も言うことはありません。

一貫して機能しているように見える唯一の解決策は、存在しない列/テーブル名などの構文またはその他のエラーを意図的にORMパラメーターに入れ、デバッグモードでコマンドラインでコードを実行すると、SQLエラーが出力されます。ついにフルフリッキンクエリで。それ以外の場合は、Webサーバーから実行した場合は、エラーがログファイルに表示されます。


クエリログは、少なくとも私にとってはうまく機能します。アプリケーションに他のいくつかのエラーがあるはずです
user1415066 '27

1

ティンカーを使用していて、形成されたSQLクエリをログに記録したい場合は、

$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.5  cli) by Justin Hileman
>>> DB::listen(function ($query) { dump($query->sql); dump($query->bindings); dump($query->time); });
=> null
>>> App\User::find(1)
"select * from `users` where `users`.`id` = ? limit 1"
array:1 [
  0 => 1
]
6.99
=> App\User {#3131
     id: 1,
     name: "admin",
     email: "admin@example.com",
     created_at: "2019-01-11 19:06:23",
     updated_at: "2019-01-11 19:06:23",
   }
>>>

1

これを試して:

$results = DB::table('users')->toSql();
dd($results);

注:get()は、未加工のSQLクエリを表示するためにtoSql()に置き換えられました。


1

これを行う私の方法は、ログビューに基づいて、ファイルを変更するだけですapp/Providers/AppServiceProvider.php

  1. このコードをに追加 app/Providers/AppServiceProvider.php
/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    //
    DB::listen(function ($query) {
        $querySql = str_replace(['?'], ['\'%s\''], $query->sql);
        $queryRawSql = vsprintf($querySql, $query->bindings);
        Log::debug('[SQL EXEC]', [
                "raw sql"  => $queryRawSql,
                "time" => $query->time,
            ]
        );
    });
}
  1. 私のSQLハンドルコード:
$users = DB::table('users')
    ->select(DB::raw('count(*) as user_count, username '))
    ->where('uid', '>=', 10)
    ->limit(100)
    ->groupBy('username')
    ->get()
;
dd($users);
  1. ログを見るstorage/logs/laravel-2019-10-27.log
[2019-10-27 17:39:17] local.DEBUG: [SQL EXEC] {"raw sql":"select count(*) as user_count, username  from `users` where `uid` >= '10' group by `username` limit 100","time":304.21} 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.