ThinkPHP 6 需开启 app_debug=true 并配置 'log' => true,调用 getLastSql() 必须紧跟查询执行方法(如 select()),且仅对同一查询实例有效;其返回带问号占位符的SQL,真实参数需通过日志或 getRealSql() 获取。

ThinkPHP 6 如何开启 SQL 日志并拿到最后执行的语句
默认情况下,ThinkPHP 不会暴露原始 SQL,必须主动启用调试模式并配置日志驱动。不配 app_debug = true 或没开数据库日志,getLastSql() 返回空字符串或上一条缓存结果,不是真实刚执行的那条。
- 确保
app_debug在app.php或环境变量中设为true - 数据库配置里加上
'log' => true(TP6.0+ 默认开启,但某些部署环境可能被覆盖) - 调用
getLastSql()必须紧跟在查询方法之后,比如$query->select(); $sql = $query->getLastSql();,中间不能穿插其他查询或事务操作 - 注意:模型静态方法如
UserModel::where(...)->select()返回的是数组,无法链式调用getLastSql();得改用UserModel::query()->where(...)->select()或先构建查询对象
为什么 getLastSql() 总是返回 null 或旧 SQL
这不是 Bug,而是 ThinkPHP 查询对象生命周期和懒执行机制导致的。SQL 真正生成是在执行(如 select()、find()、update())那一刻,而不是构建条件时。如果只写 where()->order() 没执行,getLastSql() 拿不到任何东西。
- 常见错误:在
foreach循环里反复调用getLastSql()却没对每次查询单独捕获——因为同一个查询对象复用,最后一次执行才覆盖 SQL -
Db::table('user')->where('id', 1)->find()后再调Db::getLastSql()是有效的;但Db::name('user')->where(...)->select()要用Db::name('user')->getLastSql(),不能混用静态门面和实例方法 - 使用事务时,
getLastSql()只反映最近一次执行语句,不是整个事务的汇总
TP6 中获取完整 SQL(含参数绑定值)的替代方案
getLastSql() 返回的是带问号占位符的预处理语句,比如 SELECT * FROM user WHERE id = ?。要看到真实执行的 SQL(如 WHERE id = 5),得手动替换参数,或者换用日志监听。
- 最稳的方式:打开数据库日志,配置
'log' => ['type' => 'file', 'level' => ['sql']],然后查runtime/log/xxxx/sql.log,里面每条都带展开后的参数 - 临时调试可用
think\db\Connection::getRealSql($sql, $bind),但需要自己从查询对象里捞出$bind数组,TP6 没公开这个接口,得反射或打点进Query类的私有属性$bind - 注意:开启完整 SQL 日志会影响性能,生产环境严禁开启
level => ['sql']
TP5.1 与 TP6 的 getLastSql() 行为差异
TP5.1 的 Db::getLastSql() 是静态方法,全局有效;TP6 改为实例方法,绑定到具体查询对象,更严格但也更容易误用。
立即学习“PHP免费学习笔记(深入)”;
- TP5.1:
Db::table('user')->where('id', 1)->find(); echo Db::getLastSql();✅ 有效 - TP6:
$db = Db::table('user'); $db->where('id', 1)->find(); echo $db->getLastSql();✅ 有效;但Db::getLastSql()❌ 报错或返回空 - TP6 中
model()->getLastSql()仅在使用model()->query()构建的对象上有效,直接调模型静态方法(如UserModel::select())不产生可访问的查询实例
真正难的不是怎么取 SQL,而是在嵌套查询、关联加载、软删除自动追加条件这些场景下,搞清「最后执行」到底指哪一层的语句。别依赖一次 getLastSql() 就以为拿到了全部,多打几个断点看 Query 对象的生命周期。



















