如何获取查询生成器以字符串形式输出其原始SQL查询?


544

给出以下代码:

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

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

我该怎么做呢?


14
Laravel Eloquent ORM得到原始查询:echo User::where('status', 1)->toSql();
Muhammad Shahzad

我正在为Laravel-Telescope使用一个数据包,它记录了所有查询并做了许多其他事情。

Answers:


658

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

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
嗯,我不确定,但是您可以使用作曲家软件包来完成您想要的东西stackoverflow.com/a/17339752/813181
jfortunato

9
最好使用以下Log类将其输出到应用程序的日志中: Log::debug(DB::getQueryLog())
msturdy 2013年

35
您可能需要启用此功能,因为默认情况下已将其关闭。您可以使用以下命令临时将其打开:DB::enableQueryLog();
Joshua Fricke

5
我尝试了你的答案。我尝试过的是DB::enableQueryLog(); dd(DB::getQueryLog());但它只是返回[]....
我是最愚蠢的人

6
如果您有多个数据库,则可能需要做DB::connection('database')->getQueryLog()
DamienÓCeallaigh

744

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

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

从用户中选择*

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


6
我认为这是在外部使用雄辩的Laravel时最简单的方法
Gab

8
@Stormsson这是不可能的,因为PHP永远不会用绑定替换其值的查询。要获取全部查询,您需要从MySQL记录它们。这里有更多的信息:stackoverflow.com/questions/1786322/...
马修

40
您可以使用@Stormsson getBindings方法。这将返回绑定,以便将其绑定到SQL语句。
danronmoon,2015年

2
Eloquent拒绝运行复杂的查询,因为这些查询未显示在查询日志中,这对于调试这些查询非常有用。
BobChao87 '16

33
使用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());
单线

这应该是包含在框架作为本机的功能..感谢
托马什Mleziva

请注意,如果您的查询已具有百分号(例如LIKE查询或格式化日期),则此功能将不起作用。您需要先使用双百分号将它们转义。
未知开发人员

执行此操作时是否存在安全问题?绑定是否从中清除$builder->getBindings()
solidau

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是一个产生给定变量的转储并结束脚本执行的函数,并且还导入了Event,包括use Illuminate\Support\Facades\Event;
radtek 2015年

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;
}

编辑

默认情况下,更新的版本似乎已禁用查询日志记录(以上返回空数组)。要重新打开,请在初始化Capsule Manager时获取连接实例并调用enableQueryLog方法

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

再次编辑

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

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

我从查询“ name = [{” name“:” rifat“}]”“中得到这种类型的返回信息,我只需要获取” name = rifat“才需要做什么?
incorporeal

我将打印出您的绑定,看起来您正在传递数组而不是字符串
Luke Snowden

这是一个有用的开始,但似乎忽略了在参数化值周围添加单引号,例如当我传递类似的字符串时'US/Eastern'
瑞安

1
@Ryan这是正确的,所以我为什么这么说quick function。我相信底层代码将使用prepare(php.net/manual/en/mysqli.prepare.php)方法,这就是为什么仅?需要的原因。您可以php.net/manual/en/function.is-numeric.php确定是否将输入封装在单引号内。
卢克·斯诺登

1
@LukeSnowden您的回答是天才!我终于花时间尝试了您的新版本(我在上面进行了编辑以包含您的is_numeric想法),它可以正常工作!我喜欢这个。谢谢。
Ryan

36

有一种雄辩的方法来获取查询字符串。

toSql()

就我们而言

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

返回

select * from users

是返回SQL查询字符串的确切解决方案。希望此对您有帮助...


11
查询绑定呢?例如,当您执行操作时,->where('foo', '=', 'bar')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;
}

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

照亮\数据库\口才\生成器

照亮\数据库\口才\关系\有很多

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


答案得到了改进,包括了评论中的所有评论。非常感谢你。
Yevgeniy Afanasyev

13

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

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

非常有用的答案
Anoop PS

嗨,我已经使用$ result = DB :: select('从sqrt_user_modules中选择*,其中user_id =:id',['id'=> $ user]); DB :: enableQueryLog(); 但没有得到任何输出dd(DB :: getQueryLog());
Anoop PS

我们需要包括任何图书馆吗
Anoop PS

1
步骤1:DB :: enableQueryLog(); 步骤2:$ result = DB :: select('从sqrt_user_modules中选择*,其中user_id =:id',['id'=> $ user]); 步骤3:dd(DB :: getQueryLog());
拉维·马内

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文档中进行设置

第二种方式:

另一种方法是

DB::getQueryLog()

但是,如果它返回一个空数组,则默认情况下它是禁用的,请访问this

只需启用,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. 为雄辩的生成器添加别名。(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())

    例如雄辩的建造者

    \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());

哎呀,我来晚了。只想使用Macro提交答案。这是最好的答案。应该是公认的答案:D
nmfzone '18

您可以在基础模型
Ogier Schelvis

8

使用debugbar包

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

在此处输入图片说明


7

从幼虫5.2开始。您可以DB::listen用来获取执行的查询。

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

或者,如果要调试单个Builder实例,则可以使用toSql方法。

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

1
监听是有用的,在运行查询之前先声明它,然后转储方法中的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_at,然后created_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_idpivot_set_ideqtype_jobsjob_idpivot_job_ideqtype_jobscreated_atspivot_created_atseqtype_jobsupdated_atpivot_updated_ateqtype_jobsidpivot_idjobs内连接eqtype_jobsjobsid= eqtype_jobsjob_id其中 eqtype_jobsset_id= 56为了通过pivot_created_at递减限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

现在,只需s从created_at中删除多余的内容,然后在任何SQL编辑器(例如phpMyAdmin 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

比列出的其他答案更好。
哈米斯·汗

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;
}

输出:“从选择* user,其中lang=‘EN’和status由=‘1’的顺序updated_at递减限25偏移0”


3

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

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

当您没有查询错误时,该软件包非常有用。如果您遇到SQL错误,它将不会显示任何内容
lewis4u

3

打印上一个查询

DB::enableQueryLog();

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

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扩展,它在开发人员工具的新面板上进行了扩展,该面板提供了对调试和分析PHP应用程序有用的各种信息,包括有关请求,标头,获取和发布数据,cookie,会话数据,数据库查询,路线,应用程序运行时的可视化等。

但也可以在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错误最后用完整的frickin查询。否则,如果从Web服务器运行,则希望该错误出现在日志文件中。


查询日志至少对我有用。您的应用程序中应该还有一些其他错误
user1415066 '18

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

我的操作方式是基于日志视图,只需要修改文件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} 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.