如何让查询生成器将其原始SQL查询输出为字符串?

给定以下代码:

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

我想获取上面的数据库查询生成器将生成的原始SQL查询字符串。在这个例子中,它将是SELECT * FROM users

我该怎么做?

1112778 次浏览

要将上次运行的查询输出到屏幕,您可以使用以下命令:

\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"}}

(感谢下面的约书亚的评论。

您可以监听'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"}

QueryBuilder实例上使用toSql()方法。

DB::table('users')->toSql()将返回:

从用户中选择*

这比连接事件侦听器更容易,并且还可以让您在构建查询时随时检查查询的实际外观。

注意:此方法适用于查询生成器或Eloquent,但使用toSql()而不是first()get()。您不能使用此方法同时运行查询并获取SQL。

如果你正在尝试使用Illight在没有Laravel的情况下获取日志,请使用:

\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) :
if (is_bool($binding)) {$val = $binding === true ? 'TRUE' : 'FALSE';} else if (is_numeric($binding)) {$val = $binding;} else {$val = "'$binding'";}
$prep = preg_replace("#\?#", $val, $prep, 1);endforeach;$formattedQueries[] = $prep;endforeach;return $formattedQueries;}

编辑

更新的版本似乎默认禁用了查询日志记录(上面返回一个空数组)。要重新打开,在初始化胶囊管理器时,获取连接的实例并调用enableQueryLog方法

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

再编辑一次

考虑到实际问题,您实际上可以执行以下操作来转换当前单个查询而不是之前的所有查询:

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

您可以使用此包获取加载页面时正在执行的所有查询

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

如果您不使用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 insertionforeach ($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 queriesecho 'SQL: [' . $query . ']';});
$capsule->setEventDispatcher($events);

您可以使用发条

Clockwork是PHP开发的Chrome扩展,扩展开发人员工具与一个新的面板,提供各种有用的信息调试和分析您的PHP应用程序,包括有关请求,标头,获取和发布数据,cookie,会话数据,数据库查询,路由,应用程序运行时的可视化等信息。

但也适用于Firefox

我创建了一些简单的函数来获取一些查询的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 itif( !$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 loggingif( !$logging ) DB::disableQueryLog();
// if callback returns a builder object, return the sql and bindings of itif( $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 ],// ]

在eloquent中有一个获取查询字符串的方法。

toSql()

在我们的案例中,

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

返回

select * from users

是返回SQL查询字符串的确切解决方案…希望这有帮助…

如果您使用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;}

作为输入参数,您可以使用以下任何一个

照亮\数据库\雄辩\生成器

照亮\数据库\雄辩\关系\有许多

照亮\数据库\查询\生成器

这是我放在基本模型类中的函数。只需将查询生成器对象传递给它,就会返回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;}

只有在使用$builder->get()执行查询后,DB::QueryLog()才有效。

如果您想在执行查询之前或不执行查询时获取原始查询,您可以使用$builder->toSql()方法。

获取原始SQL并将“?”替换为实际绑定值的示例:

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

或者您可以故意触发错误,例如通过使用不存在的表或列。然后您可以在异常消息中看到生成的查询。

第一种方式:

简单地说,你可以使用#0方法做以下事情,

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

如果它不起作用,你可以从laravel留档设置这个东西。

第二种方式:

另一种方法是

DB::getQueryLog()

但如果它返回一个空数组,则默认禁用访问这个

只需启用DB::enableQueryLog(),它就可以工作:)

有关更多信息,请访问Github问题以了解更多信息。

希望有帮助:)

以下是我使用的解决方案:

DB::listen(function ($sql, $bindings, $time) {$bound = preg_replace_callback("/\?/", function($matches) use ($bindings) {static $localBindings;if (!isset($localBindings)) {$localBindings = $bindings;}$val = array_shift($localBindings);
switch (gettype($val)) {case "boolean":$val = ($val === TRUE) ? 1 : 0;  // mysql doesn't support BOOL data types, ints are widely used// $val = ($val === TRUE) ? "'t'" : "'f'";   // todo: use this line instead of the above for postgres and othersbreak;
case "NULL":$val = "NULL";break;
case "string":case "object":$val = "'". addslashes($val). "'";   // correct escaping would depend on the RDBMSbreak;}return $val;}, $sql);array_map(function($x) {(new \Illuminate\Support\Debug\Dumper)->dump($x);}, [$sql, $bindings, $bound]);});

请阅读代码中的注释。我知道,它并不完美,但对于我的日常调试来说,它还可以。它试图以或多或少的可靠性构建绑定查询。但是,不要完全信任它,数据库引擎以不同的方式转义这个简短函数没有实现的值。所以,小心对待结果。

使用调试栏包

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

在此处输入图片描述

从laravel5.2开始。您可以使用DB::listen来获取执行的查询。

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

或者,如果您想调试单个Builder实例,那么您可以使用toSql方法。

DB::table('posts')->toSql();
$data = User::toSql();echo $data; //this will retrun select * from users. //here User is model

适用于laravel 5.5. X

如果您想接收应用程序执行的每个SQL查询,您可以使用监听方法。此方法可用于记录查询或调试。您可以在服务提供者中注册查询监听器:

<?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(){//}}

来源

查看Laravel执行的查询使用laravel查询日志

DB::enableQueryLog();
$queries = DB::getQueryLog();

最简单的方法是故意错误。例如,我想查看以下关系的完整SQL查询:

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

我只是为了让一列找不到,在这里我选择created_at,我通过添加尾随s将其更改为created_ats

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

因此,调试器将返回以下错误:

(4/4)错误异常SQLSTATE[42S22]:找不到列:1054未知字段列表中的“eqtype_jobs.created_ats”列(SQL:选择jobs.*,eqtype_jobs.set_id作为pivot_set_ideqtype_jobsjob_id作为pivot_job_ideqtype_jobscreated_atspivot_created_atseqtype_jobs.updated_at作为pivot_updated_ateqtype_jobs.id作为pivot_idjobs内部在jobs上加入eqtype_jobsid=eqtype_jobsjob_id其中eqtype_jobs.set_id=56按pivot_created_at desc限制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

现在,只需从created_at中删除额外的s,并在任何SQL编辑器(如phpMyAdminSQL编辑器)中测试此SQL!

注意事项:

该解决方案已用laravel5.4进行了测试。

尽管我喜欢这个框架,但我讨厌它像垃圾一样。

DB::enableQueryLog()完全没用。DB::listen同样没用。当我说$query->count()时,它显示了查询的一部分,但如果我做$query->get(),它就无话可说了。

唯一看起来一致工作的解决方案是故意在ORM参数中放一些语法或其他错误,比如不存在的列/表名称,在调试模式下在命令行上运行你的代码,它最终会用完整的查询吐出SQL错误。否则,如果从Web服务器运行,希望错误出现在日志文件中。

首先,您需要通过调用来启用查询日志:

DB::enableQueryLog();

在使用DB facade进行查询后,您可以编写:

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]]

打印最后一个查询

DB::enableQueryLog();
$query        = DB::getQueryLog();$lastQuery    = end($query);print_r($lastQuery);

宏观替换以获取具有绑定的SQL查询。

  1. AppServiceProviderboot()方法中添加以下宏函数。

    \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. Add an alias for the Eloquent Builder. (Laravel 5.4+)

    \Illuminate\Database\Eloquent\Builder::macro('toRawSql', function(){return ($this->getQuery()->toRawSql());});
  3. Then debug as usual. (Laravel 5.4+)

    E.g. Query Builder

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

    例如雄辩的建造者

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

Note: from Laravel 5.1 to 5.3, Since Eloquent Builder doesn't make use of the Macroable trait, cannot add toRawSql an alias to the Eloquent Builder on the fly. Follow the below example to achieve the same.

E.g. Eloquent Builder (Laravel 5.1 - 5.3)

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

用途:

$data = DB::select('select * from users where id = :id', ['id' => 1]);print_r($data);

输出将如下所示:

Array ( [0] => stdClass Object ( [id] => 1 [name] => parisa [last] => naderi [username] => png [password] => 2132 [role] => 0 ) )

这是我可以建议任何人调试雄辩的最后一个查询或最终查询的最佳解决方案,尽管这也已经讨论过:

// 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());
// printdd($sql);

如果您正在使用Tinker并希望记录形成的SQL查询,您可以这样做

$ php artisan tinkerPsy 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 {#3131id: 1,name: "admin",email: "admin@example.com",created_at: "2019-01-11 19:06:23",updated_at: "2019-01-11 19:06:23",}>>>

试试这个:

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

注意:get()已替换为toSql()以显示原始SQL查询。

从Laravel 5.8.15开始,查询生成器现在已经dddump方法,所以你可以这样做

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

我这样做的方式,基于日志视图,只需要修改文件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}

我通过监听查询日志并附加到日志数组来做到这一点:

//create query$query=DB::table(...)...->where(...)...->orderBy(...)...$log=[];//array of log lines...//invoked on query execution if query log is enabledDB::listen(function ($query)use(&$log){$log[]=$query;//enqueue query data to logs});//enable query logDB::enableQueryLog();$res=$query->get();//execute

在我看来,这将是初学者的最佳方法:

echo "<pre>";print_r($query->toSql());print_r($query->getBindings());

这里也有描述。https://stackoverflow.com/a/59207557/9573341

将此函数添加到您的应用程序并简单地调用。

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

产出:"选择*fromuser wherelang='en'andstatus='1'order byupdated_at desc限制25偏移量0"

下面是一个完美的例子:

https://laravel.com/docs/5.8/database#listening-for-query-events

打开app\Providers\AppServiceProvider.php并将以下内容添加到Boot()函数:

DB::listen(function ($query) {var_dump([$query->sql,$query->bindings,$query->time]);});

所以你不需要在每个函数中都放DB::enableQuerylog()DB::getQuerylog()

将此代码添加到您的AppServiceProvider并获取日志文件

         \DB::listen(function ($query) {\Log::info($query->sql,$query->bindings,$query->time);});

您可以使用toSql方法-最简单的方法

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

另外,如果您的查询中有绑定并希望查看带有绑定的查询。你不能使用这样的东西:

$query = DB::table('table')->whereIn('some_field', [1,2,30]);
$sql_with_bindings = str_replace_array('?', $query->getBindings(), $query->toSql());
dd($sql_with_bindings);

有很多信息已经回答了,将只是发布我自己的发现,我一直在使用,每当我需要在执行之前输出sql查询。

考虑下面的样本:

$user = DB::table('user')->where('id',1);echo $user->toSql();

echo$user->toSql()=这只会输出原始查询,但不会显示传递的参数。

要输出传递参数的查询,我们可以使用laravelgetBindings()和helperstr_replace_array,如下所示:

$queryWithParam = str_replace_array('?',$user->getBindings(),$user->toSql());echo $queryWithParam;

希望这也有帮助。

为了记录所有执行的查询,您可以使用DB::e获取QueryLog() icwDB::getQueryLog()。输出的结构如下。

[["query" => "select * from "users" where name = ?""bindings" => ["John Doe"]"time" => 0.34],...]

此外,我在这里结合了一些答案,以便获得使用编译绑定解析sql的完美函数。见下文。我甚至创建了一个实现此功能的自定义Builder类,以便执行例如User::where('name','John Doe')->parse();

function parse_sql(string $sql, array $bindings) : string{$compiled_bindings  = array_map('compile_binding', $bindings);
return preg_replace_array("/\?/", $compiled_bindings, $sql);}
function compile_binding($binding){$grammar = new MySqlGrammar;
if (is_bool($binding)){return (int)$binding; //This line depends on the database implementation}
if(is_string($binding)){return "'$binding'";}
if ($binding instanceof DateTimeInterface){return $binding->format($grammar->getDateFormat());}
return $binding;}

第一选择

当然,有一些方法可以只输出一个查询,并在phpMyAdmin或其他工具中进行调试,以了解查询的执行情况。

将查询与变量(也称为绑定)一起转储的一种好方法,您可以在项目中添加以下函数作为公共助手

function queryToSQL($query, $logQuery = true){$addSlashes = str_replace('?', "'?'", $query->toSql());
$sql = str_replace('%', '#', $addSlashes);
$sql = str_replace('?', '%s', $sql);
$sql = vsprintf($sql, $query->getBindings());
$sql = str_replace('#', '%', $sql);
if ($logQuery) {Log::debug($sql);}
return $sql;}

第二选择

这是一种替代方法,而不是转储每个查询,您可以使用望远镜,这个工具让您更深入地了解所有可能在后台触发的查询,以及每个查询与显示的所有绑定一起花费了多少时间

Laravel望远镜示例

第三种选择

laraveldebugbar是一个令人惊叹的插件,可以帮助您在微小的底部栏下调试所有内容,但这仅适用于您基于UI的活动,对于API或命令,调试的方式被错过了Telesphere成为一个很好的助手

Laravel调试栏示例