如何让查询生成器将其原始 SQL 查询输出为字符串?
Posted
技术标签:
【中文标题】如何让查询生成器将其原始 SQL 查询输出为字符串?【英文标题】:How do I get the query builder to output its raw SQL query as a string? 【发布时间】:2021-10-23 16:15:44 【问题描述】:给定以下代码:
DB::table('users')->get();
我想获取上面的数据库查询生成器将生成的原始 SQL 查询字符串。在此示例中,它将是 SELECT * FROM users
。
我该怎么做?
【问题讨论】:
Laravel Eloquent ORM 获取原始查询:echo User::where('status', 1)->toSql();
我正在为 Laravel - Telescope 使用一个数据包,它会记录所有查询并执行更多操作。
所有这些答案都是从 DB 类而不是模型开始的。如果您在模型类上调用方法怎么办?
【参考方案1】:
要将上次运行的查询输出到屏幕,您可以使用以下命令:
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"
(感谢Joshua's下面的评论。)
【讨论】:
嗯,我不确定,但你可以使用作曲家包***.com/a/17339752/813181 完成你想要的 使用Log
类将其输出到应用程序的日志可能会更好:Log::debug(DB::getQueryLog())
您可能需要启用它,因为它现在默认关闭。您可以使用此命令将其临时打开:DB::enableQueryLog();
我试过你的答案。我试过的是DB::enableQueryLog(); dd(DB::getQueryLog());
但它只返回[]
....
如果你有多个数据库,你可能需要做DB::connection('database')->getQueryLog()
【参考方案2】:
您可以收听“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"
【讨论】:
我在 Laravel 4 中调用了未定义的方法 Illuminate\Database\Query\Builder::listen() 谢谢,太好了。值得注意的是 dd 是一个函数,它产生一个 Dump 给定变量并结束脚本的执行以及导入事件,包括use Illuminate\Support\Facades\Event;
@radtek:您可以简单地使用use Event;
而不是use Illuminate\Support\Facades\Event;
,因为它是facade。【参考方案3】:
在QueryBuilder
实例上使用toSql()
方法。
DB::table('users')->toSql()
会返回:
从“用户”中选择 *
这比连接事件侦听器更容易,并且还可以让您在构建查询时随时检查查询的实际外观。
注意:此方法适用于查询构建器或 Eloquent,但使用 toSql()
代替 first()
或 get()
。使用此方法无法同时运行查询并获取 SQL。
【讨论】:
我认为这是在 Laravel 之外使用 Eloquent 时最简单的方法 @Stormsson 这是不可能的,因为 php 从来没有将绑定的查询替换为它们的值。要获取完整的查询,您需要从 mysql 记录它们。这里有更多信息:***.com/questions/1786322/… @Stormsson 你可以使用getBindings
方法。这将返回绑定,以便将它们绑定到 SQL 语句。
使用绑定查询$query = \DB::table('users')->where('id', 10); $sql = str_replace_array('?', $query->getBindings(), $query->toSql()); dd($sql);
for laravel 6 $query = \DB::table('users')->where('id', 10); Str::replaceArray('?', $query->getBindings(), $query->toSql());
输出 select * from users where id = 10
【参考方案4】:
如果你试图在不使用 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) :
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;
编辑
更新版本似乎默认禁用查询日志记录(上面返回一个空数组)。要重新开启,在初始化 Capsule Manager 时,抓取一个连接实例并调用 enableQueryLog
方法
$capsule::connection()->enableQueryLog();
再次编辑
考虑到实际问题,您实际上可以执行以下操作来转换当前单个查询而不是所有先前的查询:
$sql = $query->toSql();
$bindings = $query->getBindings();
【讨论】:
我从查询 "name = ["name":"rifat"]" 得到这种类型的返回,我需要做什么才能只得到 "name = rifat"? 我会打印出你的绑定,看起来你传递的是一个数组而不是一个字符串 这是一个有用的开始,但它似乎忽略了在参数化值周围添加单引号,例如当我传递像'US/Eastern'
这样的字符串时。
@Ryan 这是真的,所以我说quick function
。我相信底层代码将使用准备(php.net/manual/en/mysqli.prepare.php)方法,这就是为什么只需要?
。您可以php.net/manual/en/function.is-numeric.php 来确定是否将输入封装在单引号内。
@LukeSnowden 你的答案是天才!我终于花时间试用了您的新版本(我在上面进行了编辑以包含您的 is_numeric
想法),并且它有效!我喜欢这个。谢谢。【参考方案5】:
您可以使用此包获取加载页面时正在执行的所有查询
https://github.com/barryvdh/laravel-debugbar
【讨论】:
当您没有查询错误时,该包很好。如果您有 SQL 错误,它不会显示任何内容【参考方案6】:如果你没有使用 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);
【讨论】:
【参考方案7】:你可以使用clockwork
Clockwork 是一个用于 PHP 开发的 Chrome 扩展,它通过一个新面板扩展了开发者工具,提供了对调试和分析您的 PHP 应用程序有用的各种信息,包括有关请求、标头、获取和发布数据、cookie、会话数据、数据库查询、路由、应用程序运行时的可视化等等。
但也可以在 Firefox 中使用
【讨论】:
【参考方案8】:我创建了一些简单的函数来从一些查询中获取 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 ],
// ]
【讨论】:
【参考方案9】:eloquent 中有一个获取查询字符串的方法。
toSql()
在我们的例子中,
DB::table('users')->toSql();
返回
select * from users
是返回 SQL 查询字符串的确切解决方案..希望这有帮助...
【讨论】:
查询绑定呢?例如当你这样做时->where('foo', '=', 'bar')
栏不会显示在 sql 中【参考方案10】:
如果你使用 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
照亮\数据库\查询\构建器
【讨论】:
Answer 已改进以包括在 cmets 中所做的所有备注。非常感谢。【参考方案11】:这是我放置在我的基础模型类中的函数。只需将查询构建器对象传递给它,就会返回 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;
【讨论】:
【参考方案12】: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();
或者您可以故意触发错误,例如,通过使用不存在的表或列。然后就可以在异常信息中看到生成的查询了。
【讨论】:
这是迄今为止最好的答案,简单明了。谢谢:) 单线:$query = vsprintf(str_replace(array('?'), array('\'%s\''), $builder->toSql()), $builder->getBindings());
这应该作为原生函数包含在框架中。谢谢
请注意,如果您的查询已经包含百分号(例如 LIKE
查询或格式化日期时),这将不起作用。你需要先用双百分号转义那些。
执行此操作时是否存在安全问题?绑定是否来自$builder->getBindings()
?【参考方案13】:
第一种方式:
您只需使用 toSql()
方法即可完成以下操作,
$query = DB::table('users')->get();
echo $query->toSql();
如果它不起作用,您可以从laravel documentation 设置。
第二种方式:
另一种方法是
DB::getQueryLog()
但如果它返回一个空数组,那么默认情况下它会被禁用visit this,
只需使用DB::enableQueryLog()
启用即可:)
欲了解更多信息,请访问 Github Issue 了解更多信息。
希望对你有帮助:)
【讨论】:
【参考方案14】:这是我使用的解决方案:
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 others
break;
case "NULL":
$val = "NULL";
break;
case "string":
case "object":
$val = "'". addslashes($val). "'"; // correct escaping would depend on the RDBMS
break;
return $val;
, $sql);
array_map(function($x)
(new \Illuminate\Support\Debug\Dumper)->dump($x);
, [$sql, $bindings, $bound]);
);
请阅读代码中的 cmets。我知道,它并不完美,但对于我的日常调试来说还可以。它尝试以或多或少的可靠性构建绑定查询。但是,不要完全相信它,数据库引擎以不同的方式转义这个短函数未实现的值。所以,请谨慎对待结果。
【讨论】:
【参考方案15】:使用调试栏包
composer require "barryvdh/laravel-debugbar": "2.3.*"
【讨论】:
【参考方案16】:从 laravel 5.2
开始。您可以使用DB::listen
来执行查询。
DB::listen(function ($query)
// $query->sql
// $query->bindings
// $query->time
);
或者,如果您想调试单个 Builder
实例,则可以使用 toSql
方法。
DB::table('posts')->toSql();
【讨论】:
listen这个东西很有用,在运行查询之前声明它,并在方法中转储sql和绑定。不完美,但比其他答案更快/更容易。【参考方案17】:$data = User::toSql();
echo $data; //this will retrun select * from users. //here User is model
【讨论】:
这样更精确、更可控,更符合问题的需要。 感谢您的评论。 您可以附加->toSql()
,就像模型后面有更多参数一样。例如User::where('id', 1)->toSql()
【参考方案18】:
适用于 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()
//
Source
【讨论】:
【参考方案19】:要查看 Laravel 执行的查询,请使用 laravel 查询日志
DB::enableQueryLog();
$queries = DB::getQueryLog();
【讨论】:
【参考方案20】:最简单的方法是犯故意的错误。例如,我想查看以下关系的完整 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) ErrorException SQLSTATE[42S22]:找不到列:1054 未知 “字段列表”中的列“eqtype_jobs.created_ats”(SQL:选择
jobs
.*,eqtype_jobs
.set_id
作为pivot_set_id
,eqtype_jobs
.job_id
aspivot_job_id
,eqtype_jobs
.created_ats
作为pivot_created_ats
,eqtype_jobs
.updated_at
作为pivot_updated_at
,eqtype_jobs
.id
aspivot_id
fromjobs
inner 在jobs
.id
上加入eqtype_jobs
=eqtype_jobs
.job_id
eqtype_jobs
.set_id
= 56 order bypivot_created_at
desc limit 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 编辑器(例如 phpMyAdmin SQL 编辑器)中测试此 SQL!
注意:
该解决方案已使用 Laravel 5.4 进行测试。
【讨论】:
这是迄今为止最好的答案!很简单! :) 这不会显示带有绑定的查询,即绑定将显示为:id
@ShanthaKumara 确实,我不知道你使用的 Laravel 的版本或配置是什么。但是,我的答案中的每个 sn-p 或代码都是从 Laravel 5.4 项目的实际代码输出中复制和粘贴的。【参考方案21】:
尽管我很喜欢这个框架,但我讨厌它表现得像废话。
DB::enableQueryLog()
完全没用。 DB::listen
同样没用。当我说$query->count()
时,它显示了部分查询,但如果我说$query->get()
,则无话可说。
似乎始终有效的唯一解决方案是故意在 ORM 参数中放置一些语法或其他错误,例如不存在的列/表名称,在调试模式下在命令行上运行您的代码,它会吐出最后,完整的 frickin 查询的 SQL 错误。否则,如果从 Web 服务器运行,希望错误会出现在日志文件中。
【讨论】:
查询日志至少对我来说很好用。您的应用程序中应该还有其他一些错误【参考方案22】:首先您需要通过调用启用查询日志:
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
]
]
【讨论】:
非常有帮助的答案 嗨,我使用了 $result = DB::select('select * from sqrt_user_modules where user_id = :id', ['id' => $user]); DB::enableQueryLog();但没有得到任何输出 dd(DB::getQueryLog()); 我们是否需要包含任何库 第 1 步:DB::enableQueryLog();第 2 步:$result = DB::select('select * from sqrt_user_modules where user_id = :id', ['id' => $user]);第 3 步:dd(DB::getQueryLog());【参考方案23】:打印最后一个查询
DB::enableQueryLog();
$query = DB::getQueryLog();
$lastQuery = end($query);
print_r($lastQuery);
【讨论】:
【参考方案24】:'macroable' 替换以获取带有绑定的 SQL 查询。
在 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());
);
为 Eloquent Builder 添加一个别名。 (Laravel 5.4+)
\Illuminate\Database\Eloquent\Builder::macro('toRawSql', function()
return ($this->getQuery()->toRawSql());
);
然后像往常一样调试。 (Laravel 5.4+)
例如查询生成器
\Log::debug(\DB::table('users')->limit(1)->toRawSql())
例如雄辩的建设者
\Log::debug(\App\User::limit(1)->toRawSql());
注意:从 Laravel 5.1 到 5.3,由于 Eloquent Builder 不使用
Macroable
特征,因此无法动态添加toRawSql
别名到 Eloquent Builder。按照下面的例子来实现同样的效果。
例如Eloquent Builder (Laravel 5.1 - 5.3)
\Log::debug(\App\User::limit(1)->getQuery()->toRawSql());
【讨论】:
哎呀,我来晚了。只想使用宏提交答案。这是最好的答案。应该是公认的答案:D 您可以在基础模型的范围内抽象出后者【参考方案25】:用途:
$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 ) )
【讨论】:
他不是在问这件事。请先检查问题。【参考方案26】:这是我可以建议任何人调试 eloquent last query 或 final query 的最佳解决方案,尽管这也已讨论过:
// 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);
【讨论】:
您需要在代码顶部使用以下行:对于初学者使用 Illuminate\Support\Str;使用 Str 类【参考方案27】:如果你正在使用 tinker 并且想要记录形成的 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",
>>>
【讨论】:
【参考方案28】:试试这个:
$results = DB::table('users')->toSql();
dd($results);
注意:get() 已替换为 toSql() 以显示原始 SQL 查询。
【讨论】:
【参考方案29】:从 Laravel 5.8.15 开始,查询构建器 now has dd
和 dump
方法可以做到
DB::table('data')->where('a', 1)->dump();
【讨论】:
谢谢。 dd 效果很好。 DB::table('data')->where('a', 1)->dd(); 比列出的其他答案更好。【参考方案30】:我的做法,基于日志视图,只需要修改文件app/Providers/AppServiceProvider.php
:
-
将此代码添加到
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,
]
);
);
-
我的sql句柄代码:
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, username '))
->where('uid', '>=', 10)
->limit(100)
->groupBy('username')
->get()
;
dd($users);
-
见日志
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
【讨论】:
以上是关于如何让查询生成器将其原始 SQL 查询输出为字符串?的主要内容,如果未能解决你的问题,请参考以下文章
我如何将 SQL 原始查询重写为 Laravel 查询生成器
如何在执行查询之前从Laravel的查询生成器获取原始查询字符串?
如何将原始 SQL 查询转换为 Silverstripe SQLQuery 抽象层