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

问题描述 投票:401回答:27

给出以下代码:

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

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

我该怎么做呢?

php sql laravel laravel-4 laravel-query-builder
27个回答
475
投票

要输出到上次运行的查询,您可以使用:

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

// Your Eloquent query

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评论。)


8
投票

从laravel 5.2开始。你可以使用DB::listen来获取执行的查询。

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

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

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

8
投票

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

DB::enableQueryLog();

在使用数据库外观查询后,您可以编写:

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

7
投票

使用debugbar包

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

enter image description here


6
投票

To See Laravel Executed Query use laravel query log

DB::enableQueryLog();

$queries = DB::getQueryLog();

6
投票

用于获取带有绑定的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()) 例如。雄辩的建设者 \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());

6
投票

最简单的方法是犯故意的错误。例如,我想看到以下关系的完整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_jobs.set_idpivot_set_ideqtype_jobs.job_idpivot_job_ideqtype_jobscreated_atspivot_created_atseqtype_jobs.updated_atpivot_updated_ateqtype_jobs.idpivot_idjobs内部联接上eqtype_jobs.jobs = id.eqtype_jobs其中job_id.eqtype_jobs通过set_id降序限制= 56顺序pivot_created_at 20偏移0)(查看:/家庭/所述/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!

Notice:

该解决方案已经过Laravel 5.4测试。


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

Source


2
投票

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

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

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

614
投票

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

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

从`users`中选择*

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


2
投票

你可以使用clockwork

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

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

1
投票

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

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

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


0
投票

这是我使用的解决方案:

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

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


0
投票

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

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

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

// print
dd($sql);

0
投票

您需要在sql输出中添加绑定才能使其可读。您可以使用以下代码打印原始SQL查询:

$users = User::where('status', 1);
$users_query = str_replace(array('?'), array('\'%s\''), $users->toSql());
$users_query = vsprintf($query, $users->getBindings());
dump($users_query);

$all_users = $users->get();

-2
投票

使用:

$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 ) )

54
投票

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

52
投票

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

46
投票

如果您尝试使用Illuminate而不使用Laravel来使用Log:

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

37
投票

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

toSql()

在我们的例子中,

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

返回

select * from users

是返回SQL查询字符串的确切解决方案。希望这有用...


27
投票
$data = User::toSql();
echo $data; //this will retrun select * from users. //here User is model

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

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

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

照亮\数据库\雄辩\关系\的hasMany

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


11
投票

First way:

你可以使用toSql()方法做以下事情,

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

echo $query->toSql();

如果它不起作用你可以从laravel documentation设置东西。

Second way:

另一种方法是

DB::getQueryLog()

但如果它返回一个空数组,那么默认情况下它被禁用visit this

只需启用DB::enableQueryLog(),它将工作:)

欲了解更多信息,请访问Github Issue了解更多信息。

希望能帮助到你 :)

© www.soinside.com 2019 - 2024. All rights reserved.