如何从 PHP 的 Query Builder 中提取原始 SQL:3 种实用方法

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

“医生,我的 Query Builder 生成了些奇怪的东西……”

熟悉的场景:您编写了一个优雅的链式 Query Builder 查询,确信一切完美运行,但结果要么报错,要么数据不对。任何 PHP 开发者首先想到的都是:“让我看看你到底生成了什么!”

“How do I get the query builder to output its raw SQL query as a string?” 这个问题是 Stack Overflow 上最热门的问题之一(数千次浏览和数十个回答)。这并非偶然。能够快速获取纯 SQL 是一项超能力,可以节省数小时的调试时间。

在本文中,我们将介绍 3 种实用方法,适用于 PHP 框架(Laravel、Doctrine、CakePHP),展示真实的代码示例,并解释何时使用哪种方法。开始吧!



为什么需要这个?(简明扼要)

  • 调试 — 理解查询为何返回错误数据。
  • 优化 — 查看实际执行的 JOIN 和 WHERE 条件。
  • 学习 — 新手常常不理解 Query Builder 如何将方法转换为 SQL。
  • 日志记录 — 保存原始查询用于审计或重放。

准备好了吗?那我们直接看代码!



方法 1:Laravel Eloquent / Query Builder — 经典方法

在 Laravel 中有几种方法,但最可靠的是结合使用 toSql() 和 getBindings()。为什么要一起用?因为 toSql() 显示的是带有占位符 ? 的查询,不替换值的话毫无用处。



简单示例:带 WHERE 的 SELECT

use App\\Models\\User;

$query = User::where('email', 'like', '%@gmail.com') ->where('active', 1) ->orderBy('created_at', 'desc');

// 获取带占位符的 SQL$sql = $query->toSql();$bindings = $query->getBindings();

// 组装成可读的形式$fullSql = vsprintf(str_replace('?', '%s', $sql), $bindings);

echo $fullSql;// 结果:// SELECT * FROM users WHERE email LIKE '%@gmail.com' AND active = 1 ORDER BY created_at DESC

重要提示: vsprintf 并非万能。对于复杂数据类型(例如 NULL 或数组),最好使用 Str::replaceArray() 或 laravel-query-builder-debug 包。



进阶版本:带 join 和 whereIn

$query = DB::table('orders')    ->join('users', 'orders.user_id', '=', 'users.id')    ->whereIn('orders.status', ['pending', 'processing'])    ->where('orders.total', '>', 100)    ->select('orders.*', 'users.name');

$bindings = $query->getBindings();$sql = $query->toSql();

// 美观地格式化$fullSql = preg_replace_array('/\\?/', $bindings, $sql);echo $fullSql;// SELECT orders.*, users.name FROM orders INNER JOIN users ON orders.user_id = users.id WHERE orders.status IN ('pending', 'processing') AND orders.total > 100

建议: 使用 preg_replace_array() — 它比 vsprintf 更安全,尤其是当数据中包含 % 字符时。



方法 2:Doctrine DBAL — 适合喜欢严谨的人

Doctrine Query Builder(常用于 Symfony)稍微复杂一些。这里没有现成的 toSql(),但有 getSQL() 和 getParameters()。另外还需要考虑参数类型。



基础示例

use Doctrine\\DBAL\\Connection;use Doctrine\\DBAL\\Query\\QueryBuilder;

/** @var Connection $connection */$queryBuilder = $connection->createQueryBuilder();$queryBuilder ->select('p.*', 'c.name as category_name') ->from('products', 'p') ->join('p', 'categories', 'c', 'p.category_id = c.id') ->where('p.price > :minPrice') ->andWhere('p.sto

博客

书籍推荐