Laravel 5: 如何输出 SQL 查询语句?

6

Laravel 5内置解决方案

在Laravel 5+中,我们可以使用\DB::getQueryLog()来检索执行的所有查询。由于查询日志是一项繁琐的操作,并且可能会导致性能问题,因此在L5中默认禁用它,并仅推荐在开发环境中使用。我们可以通过使用方法\DB::enableQueryLog()来启用查询日志,如[Laravel文档][1]中所述。

内置解决方案中存在的问题

DB::getQueryLog()函数非常方便,但有时我们希望以平面SQL格式获得转储,这样我们就可以将其复制/粘贴到我们喜欢的MySQL应用程序中(如phpMyAdminSqlyog)进行调试或优化。

所以,我需要一个辅助函数,帮助我生成带有以下附加信息的转储:

  • 产生转储的文件和行号。
  • 从查询中删除反引号。
  • 平面查询,因此无需手动更新绑定参数,可以在phpMyAdmin等中复制/粘贴SQL以调试/优化查询。
5个回答

4

定制解决方案

步骤1:启用查询日志记录

将以下代码块复制/粘贴到路由文件的顶部:

# File: app/Http/routes.php
if (\App::environment( 'local' )) { 
   \DB::enableQueryLog();
}

步骤2:添加辅助函数

if (!function_exists( 'dump_query' )) {
function dump_query( $last_query_only=true, $remove_back_ticks=true ) {

    // location and line
    $caller = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 1 );
    $info = count( $caller ) ? sprintf( "%s (%d)", $caller[0]['file'], $caller[0]['line'] ) : "*** Unable to parse location info. ***";

    // log of executed queries
    $logs = DB::getQueryLog();
    if ( empty($logs) || !is_array($logs) ) {
        $logs = "No SQL query found. *** Make sure you have enabled DB::enableQueryLog() ***";
    } else {
        $logs = $last_query_only ? array_pop($logs) : $logs;
    }

    // flatten bindings
    if (isset( $logs['query'] ) ) {
        $logs['query'] = $remove_back_ticks ? preg_replace( "/`/", "", $logs['query'] ) : $logs['query'];

        // updating bindings
        $bindings = $logs['bindings'];
        if ( !empty($bindings) ) {
            $logs['query'] = preg_replace_callback('/\?/', function ( $match ) use (&$bindings) {
                return "'". array_shift($bindings) . "'";
            }, $logs['query']);
        }
    }
    else foreach($logs as &$log) {
        $log['query'] = $remove_back_ticks ? preg_replace( "/`/", "", $log['query'] ) : $log['query'];

        // updating bindings
        $bindings = $log['bindings'];
        if (!empty( $bindings )) {
            $log['query'] = preg_replace_callback(
                '/\?/', function ( $match ) use ( &$bindings ) {
                return "'" . array_shift( $bindings ) . "'";
            }, $log['query']
            );
        }
    }

    // output
    $output = ["*FILE*" => $info,
               '*SQL*' => $logs
    ];

    dump( $output );
}

如何使用?

在查询执行后,获取最后一次执行的查询转储,并使用以下代码:

dump_query();

使用以下命令记录所有已执行的查询:

dump_query( false );

2

2
  • 转至哪个文件和行号时出现了转储。

我不明白为什么你需要这个,因为你总是知道在哪里调用了转储函数,但没关系,你有解决方案。

  • 从查询中删除back-ticks

你不需要删除back-ticks,因为查询在MySQL中与它们一起工作也可以。

  • 扁平化查询,因此不需要手动更新绑定参数,我可以将SQL复制/粘贴到phpMyAdmin等中以调试/优化查询。

你可以使用 vsprintf 来绑定参数:

$queries = DB::getQueryLog();

foreach ($queries as $key => $query) {
    $queries[$key]['query'] = vsprintf(str_replace('?', '\'%s\'', $query['query']), $query['bindings']);
}

return $queries;

我建议你去查看这个 Github 仓库 squareboat/sql-doctor


通过移除 back-ticks,生成的 SQL 将更易于阅读和调试,因为该函数的主要工作是调试。感谢 vsprintf 函数,它将帮助我减少代码量。 - Muhammad Adnan

0
对于 Laravel 8 应用程序,将以下内容放入 AppServiceProvider.php 文件中可能很有用:
/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    // [...]
    // Dump SQL queries on demand **ONLY IN DEV**
    if (env('APP_ENV') === 'local') {
        DB::enableQueryLog();
        Event::listen(RequestHandled::class, function ($event) {
            if ( $event->request->has('sql-debug') ) {
                $queries = DB::getQueryLog();
                Log::debug($queries);
                dump($queries);
            }
        });
    }

    // [...]
}

&sql-debug=1添加到URL末尾,即可转储查询。


0

将此代码添加到您的路由文件顶部。 Laravel 5.2 routes.php Laravel 5.3+ web.php

<?php
// Display all SQL executed in Eloquent

Event::listen('Illuminate\Database\Events\QueryExecuted', function ($query) {
    var_dump($query->sql);
    var_dump($query->bindings);
    var_dump($query->time);
    echo "<br><br><br>";
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接