在执行查询之前,如何从 Laravel 的查询构建器中获取原始查询字符串?

Posted

技术标签:

【中文标题】在执行查询之前,如何从 Laravel 的查询构建器中获取原始查询字符串?【英文标题】:How can I get the raw query string from Laravel's query builder BEFORE executing the query? 【发布时间】:2013-12-01 11:34:01 【问题描述】:

我有一个由几个条件创建的复杂查询,我想从即将执行的构建器对象中获取最终的 SQL 查询。我可以这样做吗?

【问题讨论】:

【参考方案1】:

你可以做到:

$query = DB::table('brands')
                ->join('products','a','=','c')
                ->whereNull('whatever');

echo $query->toSql();

但是 Laravel 不会在查询中显示参数,因为它们是在准备好查询之后绑定的。

所以你也可以这样做:

print_r( $query->getBindings() );

【讨论】:

返回带有占位符而不是实际参数的查询 要使用 getBinings() 我需要使用任何名称空间或其他东西吗? @MatteoRiva Object of class Illuminate\Database\Eloquent\Builder could not be converted to string 这就是我尝试从模型查询中获取toSQL 时得到的结果。它给了我一个 Builder 实例。如何从构建器实例中获取 sql? 原来我用的是toSQL 而不是toSql,这给了我完全不同的结果【参考方案2】:

对于调试,这可能会非常方便,因为它会返回带有绑定的 SQL,因此您可以立即将其放入数据库控制台。

/**
 * Combines SQL and its bindings
 *
 * @param \Eloquent $query
 * @return string
 */
public static function getEloquentSqlWithBindings($query)

    return vsprintf(str_replace('?', '%s', $query->toSql()), collect($query->getBindings())->map(function ($binding) 
        return is_numeric($binding) ? $binding : "'$binding'";
    )->toArray());

【讨论】:

这对我有用。比上面接受的答案要好得多。 我得到的 vsprintf 参数太少 这就是要找的东西【参考方案3】:

我想在我的查询中添加换行符和大写 mysql 关键字,因此添加到 andi79h 的答案中。不是特别优雅,我敢肯定有很多边缘情况无法使用,但我仍然觉得它很有用......

function debug_query($query) 
    $query = vsprintf(str_replace('?', '%s', $query->toSql()), collect($query->getBindings())->map(function ($binding) 
        return is_numeric($binding) ? $binding : "'$binding'";
    )->toArray());

    $double_linebreak_words = ['(', ')'];
    $double_linebreak_words_replace = array_map(function($str) return php_EOL . $str . PHP_EOL; , $double_linebreak_words);
    $query = str_replace($double_linebreak_words, $double_linebreak_words_replace, $query);

    $mysql_keywords = ['ADD', 'ALL', 'ALTER', 'ANALYZE', 'AND', 'AS', 'ASC', 'AUTO_INCREMENT', 'BDB', 'BERKELEYDB', 'BETWEEN', 'BIGINT', 'BINARY', 'BLOB', 'BOTH', 'BTREE', 'BY', 'CASCADE', 'CASE', 'CHANGE', 'CHAR', 'CHARACTER', 'CHECK', 'COLLATE', 'COLUMN', 'COLUMNS', 'CONSTRAINT', 'CREATE', 'CROSS', 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'DATABASE', 'DATABASES', 'DAY_HOUR', 'DAY_MINUTE', 'DAY_SECOND', 'DEC', 'DECIMAL', 'DEFAULT', 'DELAYED', 'DELETE', 'DESC', 'DESCRIBE', 'DISTINCT', 'DISTINCTROW', 'DIV', 'DOUBLE', 'DROP', 'ELSE', 'ENCLOSED', 'ERRORS', 'ESCAPED', 'EXISTS', 'EXPLAIN', 'FALSE', 'FIELDS', 'FLOAT', 'FOR', 'FORCE', 'FOREIGN', 'FROM', 'FULLTEXT', 'FUNCTION', 'GEOMETRY', 'GRANT', 'GROUP', 'HASH', 'HAVING', 'HELP', 'HIGH_PRIORITY', 'HOUR_MINUTE', 'HOUR_SECOND', 'IF', 'IGNORE', 'INDEX', 'INFILE', 'INNER', 'INNODB', 'INSERT', 'INTEGER', 'INTERVAL', 'INTO', 'JOIN', 'KEY', 'KEYS', 'KILL', 'LEADING', 'LEFT', 'LIKE', 'LIMIT', 'LINES', 'LOAD', 'LOCALTIME', 'LOCALTIMESTAMP', 'LOCK', 'LONG', 'LONGBLOB', 'LONGTEXT', 'LOW_PRIORITY', 'MASTER_SERVER_ID', 'MATCH', 'MEDIUMBLOB', 'MEDIUMINT', 'MEDIUMTEXT', 'MIDDLEINT', 'MINUTE_SECOND', 'MOD', 'MRG_MYISAM', 'NATURAL', 'NOT', 'NULL', 'NUMERIC', 'ON', 'OPTIMIZE', 'OPTION', 'OPTIONALLY', 'ORDER', 'OUTER', 'OUTFILE', 'PRECISION', 'PRIMARY', 'PRIVILEGES', 'PROCEDURE', 'PURGE', 'READ', 'REAL', 'REFERENCES', 'REGEXP', 'RENAME', 'REPLACE', 'REQUIRE', 'RESTRICT', 'RETURNS', 'REVOKE', 'RIGHT', 'RLIKE', 'RTREE', 'SELECT', 'SET', 'SHOW', 'SMALLINT', 'SOME', 'SONAME', 'SPATIAL', 'SQL_BIG_RESULT', 'SQL_CALC_FOUND_ROWS', 'SQL_SMALL_RESULT', 'SSL', 'STARTING', 'STRAIGHT_JOIN', 'STRIPED', 'TABLE', 'TABLES', 'TERMINATED', 'THEN', 'TINYBLOB', 'TINYINT', 'TINYTEXT', 'TO', 'TRAILING', 'TRUE', 'TYPES', 'UNION', 'UNIQUE', 'UNLOCK', 'UNSIGNED', 'UPDATE', 'USAGE', 'USE', 'USER_RESOURCES', 'USING', 'VALUES', 'VARBINARY', 'VARCHAR', 'VARCHARACTER', 'VARYING', 'WARNINGS', 'WHEN', 'WHERE', 'WITH', 'WRITE', 'XOR', 'YEAR_MONTH', 'ZEROFILL', 'INT', 'OR', 'IS', 'IN'];
    $mysql_keywords = array_map(function($str) return " $str "; , $mysql_keywords);
    $mysql_keywords_lc = array_map(function($str) return strtolower($str); , $mysql_keywords);
    $query = str_replace($mysql_keywords_lc, $mysql_keywords, $query);

    $linebreak_before_words = ['INNER JOIN', 'LEFT JOIN', 'OUTER JOIN', 'RIGHT JOIN', 'WHERE', 'FROM', 'GROUP BY', 'SELECT'];
    $linebreak_before_words_replace = array_map(function($str) return PHP_EOL . $str; , $linebreak_before_words);
    $query = str_replace($linebreak_before_words, $linebreak_before_words_replace, $query);

    $linebreak_after_words = [','];
    $linebreak_after_words_replace = array_map(function($str) return $str . PHP_EOL; , $linebreak_after_words);
    $query = str_replace($linebreak_after_words, $linebreak_after_words_replace, $query);

    $query = str_replace('select ', 'SELECT ', $query);

    return $query;

【讨论】:

【参考方案4】:

从 andi79h 的回答中回过头来。该函数运行良好,但它假设绑定没有任何可能破坏查询的引号。

我在 $bindings 中添加了“addslashes”以使其更安全一些。虽然理想情况下,如果您有一个活动连接可以使用,它应该通过 mysqli_real_escape_string() 运行。 https://www.php.net/manual/en/mysqli.real-escape-string.php

/**
 * Combines SQL and its bindings
 *
 * @param \Eloquent $query
 * @return string
 */
public static function getEloquentSqlWithBindings($query)

    return vsprintf(str_replace('?', '%s', $query->toSql()), collect($query->getBindings())->map(function ($binding) 
        $binding = addslashes($binding);
        return is_numeric($binding) ? $binding : "'$binding'";
    )->toArray());

【讨论】:

【参考方案5】:

更新@andi79h 的答案以处理查询中有 % 的情况。 (例如,DATE_FORMAT(report_date, '%c/%e/%Y') 会导致参数太少的错误。

/**
 * Combines SQL and its bindings
 *
 * @param \Eloquent $query
 * @return string
 */
public static function getEloquentSqlWithBindings($query)

        return vsprintf(str_replace('?', '%s', str_replace('%', '%%', $query->toSql())), collect($query->getBindings())->map(function ($binding) 
            return is_numeric($binding) ? $binding : "'$binding'";
        )->toArray());

【讨论】:

以上是关于在执行查询之前,如何从 Laravel 的查询构建器中获取原始查询字符串?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Laravel 的查询构建器执行嵌套连接?

如何使用查询构建在 Laravel 中执行 WHERE NOT

如何在查询构建器 laravel 上从每个组中获取多个数据

如何从两个不同的 Laravel 查询构建器中获取我的两个结果集以同时显示在同一页面上?

如何在查询生成器 laravel 中执行此 SQL

如何将原生 PHP 转换为查询构建器 Laravel?