ChatGPT解决这个技术问题 Extra ChatGPT

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

给定以下代码:

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

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

我该怎么做呢?

Laravel Eloquent ORM 获取原始查询:echo User::where('status', 1)->toSql();
我正在为 Laravel - Telescope 使用一个数据包,它记录所有查询并执行更多操作。
所有这些答案都是从 DB 类而不是模型开始的。如果您在模型类上调用方法怎么办?

s
sdexp

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

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

从“用户”中选择 *

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

注意:此方法适用于查询构建器或 Eloquent,但使用 toSql() 代替 first()get()。您无法使用此方法同时运行查询并获取 SQL。


我认为这是在 Laravel 之外使用 Eloquent 时最简单的方法
@Stormsson那是不可能的,因为PHP从来没有将绑定替换为它们的值的查询。要完整获取查询,您需要从 MySQL 记录它们。此处有更多信息:stackoverflow.com/questions/1786322/…
@Stormsson 您可以使用 getBindings 方法。这将返回绑定,以便将它们绑定到 SQL 语句。
使用绑定查询 $query = \DB::table('users')->where('id', 10); $sql = str_replace_array('?', $query->getBindings(), $query->toSql()); dd($sql);
对于 laravel 6 $query = \DB::table('users')->where('id', 10); Str::replaceArray('?', $query->getBindings(), $query->toSql()); 输出 select * from users where id = 10
F
Farid Movsumov

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

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


嗯,我不确定,但您可以使用作曲家包完成您想要的stackoverflow.com/a/17339752/813181
使用 Log 类将其输出到应用程序的日志可能会更好:Log::debug(DB::getQueryLog())
您可能需要启用它,因为它现在默认关闭。您可以使用此命令暂时打开它:DB::enableQueryLog();
我试过你的答案。我试过的是 DB::enableQueryLog(); dd(DB::getQueryLog()); 但它只返回 []....
如果您有多个数据库,您可能需要执行 DB::connection('database')->getQueryLog()
k
kmoser

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()
R
Rubens Mariuzzo

您可以收听“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
A
Anis LOUNIS aka AnixPasBesoin

如果您尝试在不使用 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。我相信底层代码将使用 prepare (php.net/manual/en/mysqli.prepare.php) 方法,这就是为什么只需要 ? 的原因。您可以 php.net/manual/en/function.is-numeric.php 确定是否将输入封装在单引号内。
@LukeSnowden 你的答案是天才!我终于花时间试用了您的新版本(我在上面进行了编辑以包含您的 is_numeric 想法),并且它有效!我喜欢这个。谢谢你。
j
jisna

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

toSql()

在我们的例子中,

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

返回

select * from users

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


查询绑定呢?例如,当您执行 ->where('foo', '=', 'bar') 时,条形不会显示在 sql 中
K
Kuldeep Mishra
$data = User::toSql();
echo $data; //this will retrun select * from users. //here User is model

这更加精确,可控,并且满足了问题的需要。
感谢您的评论。
如果模型后面有更多参数,您可以附加 ->toSql()。例如User::where('id', 1)->toSql()
l
lewis4u

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

// 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 类
C
Community

如果你使用 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


改进的答案包括评论中的所有评论。非常感谢。
J
Jignesh Joisar

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

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());
唯一明确的解决方案
I
Ijas Ameenudeen

使用绑定获取 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()) 例如 Eloquent Builder \Log::debug(\App\User: :limit(1)->toRawSql());

注意:从 Laravel 5.1 到 5.3,由于 Eloquent Builder 没有使用 Macroable trait,所以不能动态地为 Eloquent Builder 添加 toRawSql 别名。按照下面的示例来实现相同的目的。

例如 Eloquent Builder (Laravel 5.1 - 5.3)

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

哎呀,我来晚了。只想使用宏提交答案。这是最好的答案。应该是公认的答案:D
您可以在基本模型的范围内抽象出后者
G
Greg

从 Laravel 5.8.15 开始,查询构建器 now has dddump 方法让你可以做到

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

谢谢。 dd 效果很好。 DB::table('data')->where('a', 1)->dd();
比列出的其他答案更好。
C
Community

第一种方式:

您只需使用 toSql() 方法即可完成以下操作,

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

echo $query->toSql();

如果它不起作用,您可以从 laravel documentation 设置它。

第二种方式:

另一种方法是

DB::getQueryLog()

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

只需使用 DB::enableQueryLog() 启用即可:)

如需更多信息,请访问 Github Issue 以了解更多信息。

希望能帮助到你 :)


S
SaidbakR

最简单的方法是故意犯错误。例如,我想查看以下关系的完整 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]: Column not found: 1054 Unknown column 'eqtype_jobs.created_ats' in 'field list' (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)(查看:/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 的版本或配置是什么。但是,我的答案中的每个片段或代码都是从 Laravel 5.4 项目的真实代码输出中复制和粘贴的。
r
rahim.nagori

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

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

此处也对此进行了描述。 https://stackoverflow.com/a/59207557/9573341


D
Dharmik

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

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

输出: "select * from user where lang = 'en' and status = '1' order by updated_at desc limit 25 offset 0"


A
Angelito Tan

已经回答了很多信息,只要我需要在执行之前输出 sql 查询,我就会发布我自己的发现。

考虑以下示例:

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

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

要输出带有传递参数的查询,我们可以像这样使用 laravel getBindings() 和 helper str_replace_array:

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

希望这也有帮助。


J
Jignesh Joisar

使用调试栏包

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

https://i.stack.imgur.com/YQBxK.png


Z
Zayn Ali

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

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

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

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

听东西很有用,在运行查询之前声明它,并在方法中转储 sql 和绑定。不完美,但比其他答案更快/更容易。
J
Jasim Juwel

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

DB::enableQueryLog();

$queries = DB::getQueryLog();

B
BoogieBug

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

M
MTakumi

您可以使用 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);

N
Nikhil Gyan

尝试这个:

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

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


s
scre_www

对于 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


S
Spencer Williams

尽管我很喜欢这个框架,但我讨厌它表现得像废话。

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

唯一似乎始终有效的解决方案是故意在 ORM 参数中放置一些语法或其他错误,例如不存在的列/表名称,在调试模式下在命令行上运行代码,它会吐出 SQL 错误终于有了完整的 frickin 查询。否则,如果从 Web 服务器运行,希望错误出现在日志文件中。


查询日志至少对我来说很好。您的应用程序中应该还有其他一些错误
M
Mark Baaijens

为了记录所有执行的查询,您可以使用 DB::enableQueryLog() icw DB::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;
}

L
Lakhwinder Singh

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

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

当您没有查询错误时,该包很好。如果你有一个 SQL 错误,它不会显示任何东西
S
Sohomdeep Paul

打印最后一个查询

DB::enableQueryLog();

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

P
Prafulla Kumar Sahu

如果你正在使用 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",
   }
>>>

l
lupguo

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

查看日志存储/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}