如何打印 Laravel 模型查询产品的 SQL 语句?

34

我们有时候想测试一段代码生产的 SQL 语句,比如: 我们想看 App\User::all(); 产生的 SQL 语句,我们简单地使用路由闭包做个实验:

Route::get('/test-sql', function() {

    DB::enableQueryLog();

    $user = App\User::all();

    return response()->json(DB::getQueryLog());
});

然后我们在浏览器打开 http://www.example.com/test-sql 即可看到 $user = User::all(); 所产生的 SQL 了。

[
    {
        query: "select * from `users` where `users`.`deleted_at` is null",
        bindings: [ ],
        time: 1.37
    }
]

如果需要经常查看 SQL 信息,推荐使用 clockwork 这个插件更友好的记录。

讨论数量: 7
jltxwesley

补充下,如果想查看所有的 query,还可以使用 监听查询事件

DB::listen(function ($sql) { 
    var_dump($sql->sql, $sql->bindings, $query->time); 
});
3个月前

API 开发用这个包也很方便
https://github.com/overtrue/laravel-query-logger

3个月前
    DB::enableQueryLog();

    if (class_exists(QueryExecuted::class, true)) {
        DB::listen(function ($queryExecuted) {
            $query = $queryExecuted->sql;
            $bindings = $queryExecuted->bindings;
            $spend = $queryExecuted->time;
            $db = $queryExecuted->connectionName;
            $i = 0;
            $message = sprintf(
                "query %s => %s ( %s ms )",
                $db,
                preg_replace_callback('/\?/', function ($matches) use ($bindings, &$i) {
                    if (! empty($bindings[$i])) {
                        return '\''.$bindings[$i++].'\'';
                    } else {
                        return '\''.'\'';
                    }

                }, $query),
                $spend
            );
            $tmp = ['sql' => $message, 'db' => $db, 'cost' => $spend]);
           var_dump($tmp);
        });
    } else {
        DB::listen(function ($query, $bindings, $spend, $db) {
            $i = 0;
            $message = sprintf(
                "query %s => %s ( %s ms )",
                $db,
                preg_replace_callback('/\?/', function ($matches) use ($bindings, &$i) {
                    if (! empty($bindings[$i])) {
                        return '\''.$bindings[$i++].'\'';
                    } else {
                        return '\''.'\'';
                    }

                }, $query),
                $spend
            );
           $tmp = ['sql' => $message, 'db' => $db, 'cost' => $spend]);
           var_dump($tmp);
        });
    }
3个月前
sushengbuhuo

@leoyang QueryExecuted这个类哪个版本开始有的

还有能输出当前链接使用的ip和端口吗

3个月前
leililei

使用helper str_replace_array()可以把SQL和binding结合成完整的SQL语句

3个月前

可以在中间件实现

class QueryLogMiddleware
{
    public function handle($request, Closure $next)
    {
        return $next($request);
    }

    /**
     * 程序发送响应之后的操作,记录慢查询日志或查询全日志
     *
     * @param Request $request
     * @param         $response
     */
    public function terminate(Request $request, $response)
    {
        $queryLogs = \DB::getQueryLog();
        if (empty($queryLogs)) {
            return;
        }

        $currentUrl = $request->fullUrl();

        $enableFullQueryLog = config('database.enableQueryLog');

        if ($enableFullQueryLog) {
            $this->writeFullQueryLog($queryLogs, $currentUrl);
        }

        $enableSlowQueryLog = config('database.enableSlowLog');

        if ($enableSlowQueryLog) {
            $this->slowQueryLog($queryLogs, $currentUrl);
        }

    }

    public function slowQueryLog(array $data, string $url)
    {
        //大于一秒
        $slow = array_filter($data, function ($value) {
            return $value['time'] > 1000;
        });
        $slow = $this->formatQueryString($slow);

        if (!empty($slow)) {
            \Log::error('found slow query', [
                'url'  => $url,
                'sqls' => $slow
            ]);
        }
        //todo DingTalk notify and throttle limit
    }

    /**
     * 写全查询log到文件
     *
     * @param array  $data
     * @param string $url
     */
    public function writeFullQueryLog(array $data, string $url)
    {
        $logs = json_encode($this->formatQueryString($data));

        $fileName = 'logs' . DIRECTORY_SEPARATOR . "query-" . date('Y-m-d') . '.log';
        $logFile = fopen(
            storage_path($fileName),
            'a+'
        );
        fwrite($logFile, date('Y-m-d H:i:s') . ': ' . $url . " | " . $logs . PHP_EOL);
        fclose($logFile);
    }

    /**
     * 拼接SQL字符串
     *
     * @param array $data
     *
     * @return array
     */
    public function formatQueryString(array $data)
    {
        return array_map(function ($value) {
            return [
                'time' => array_get($value, 'time') . 'ms',
                'sql'  => Str::replaceArray('?', array_get($value, 'bindings'), array_get($value, 'query'))
            ];
        }, $data);
    }
}
3周前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!