Laravel 5:如何转储 SQL 查询?

Posted

技术标签:

【中文标题】Laravel 5:如何转储 SQL 查询?【英文标题】:Laravel 5: How to dump SQL query? 【发布时间】:2017-05-09 19:04:13 【问题描述】:

Laravel 5 的内置解决方案

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

内置解决方案中的问题

DB::getQueryLog() 函数很棒,但有时我们希望以平面 SQL 格式转储它会很棒,因此我们可以将其复制/粘贴到我们最喜欢的 mysql 应用程序中,例如 phpMyAdminSqlyog 来执行它并调试或优化它。

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

转储调用了哪个文件和行号。 从查询中删除反引号。 平面查询,因此不需要手动更新绑定参数,我可以在phpMyAdmin 中复制/粘贴SQL 等来调试/优化查询。

【问题讨论】:

【参考方案1】:

我一直在寻找简单的解决方案,而下面的解决方案对我有用。

DB::enableQueryLog();

User::find(1); //Any Eloquent query

// and then you can get query log

dd(DB::getQueryLog());

参考链接:

How to Get the Query Executed in Laravel 5? DB::getQueryLog() Returning Empty Array https://www.codegrepper.com/code-examples/php/dump+sql+query+laravel

【讨论】:

【参考方案2】:

对于 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 将转储查询。

【讨论】:

【参考方案3】:

将此代码添加到您的路线文件的顶部。 Laravel 5.2 路由.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>";
);

【讨论】:

【参考方案4】: 转储在哪个文件和行号上 调用。

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

从查询中删除 back-ticks

您无需删除 back-ticks,因为查询也可以在 MySQL 中与它们一起使用。

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

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

$queries = DB::getQueryLog();

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


return $queries;

我建议你查看这个 github repo squareboat/sql-doctor

【讨论】:

通过删除back-ticks,生成的SQL将易于阅读和调试,因为该函数的主要工作是调试。感谢vsprintf 功能,是的,它将帮助我减少代码。【参考方案5】:

自定义解决方案

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

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

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

第二步:添加辅助函数

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

【讨论】:

以上是关于Laravel 5:如何转储 SQL 查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何修复 laravel 5.5 中的查询生成器错误 sql 注入 *

在 laravel 5.1 中无法获取 SQL 查询

Laravel 5.7 执行 SQL 查询多少次?

在 laravel 4 中即时克隆数据库或运行 sql 转储

Laravel 5 Eloquent:如何获取正在执行的原始 sql? (带有绑定数据)

如何在 Laravel 5.8 中获取关注用户的帖子