<p>原生SQL中不要手动加表前缀,ThinkPHP 6仅对Db::name()或{%TABLE%}占位符自动替换前缀;Db::table()、裸字符串及模型中硬编码$ table均不生效,应通过Db::name('user')->query("SELECT * FROM {%TABLE%}")或Db::getTable('user')动态获取。</p>

原生查询 SQL 里**不要手动加前缀**,ThinkPHP 6 会自动处理。
Db::query() 和 Db::execute() 自动拼接前缀
当你在 database.php 中配置了 'prefix' => 'tp_',使用 Db::query() 或 Db::execute() 执行原生 SQL 时,TP6 会自动识别并替换表名中的占位符 {%TABLE%}(仅限该机制),但更常见、更推荐的是用 Db::name() 配合原生 SQL 的方式。
不过要注意:原生 SQL 字符串本身如果写死表名(如 "SELECT * FROM user"),TP6 不会自动替换成 tp_user —— 它只对 Db::name('user') 这类方法生效,不解析任意字符串里的表名。
所以正确做法是:
- 用
Db::name('user')->query("SELECT * FROM {%TABLE%} WHERE id = ?"),其中{%TABLE%}会被自动替换成tp_user - 或直接拼接:
$table = Db::getTable('user'); Db::query("SELECT * FROM {$table} WHERE id = ?", [$id]) - 避免写
Db::query("SELECT * FROM tp_user WHERE ...")—— 这样就和配置脱钩,换环境容易出错
Db::table() 不加前缀,Db::name() 才加
Db::table('user') 是字面量查询,SQL 中直接出现 user,完全忽略 prefix 配置;而 Db::name('user') 才会按配置拼上前缀变成 tp_user。
因此,如果你用原生 SQL,又想保持前缀可配,就别混用 table(),优先走 name() + 占位符,或通过 Db::getTable('user') 获取带前缀的表名再拼入。
模型中写死表名会绕过所有前缀配置
如果在模型里写了 protected $table = 'tp_user';,那无论你怎么改 database.php 的 prefix,都无效。这个值会直接进 SQL,不参与任何拼接逻辑。
所以原生查询是否要加前缀,关键看你是怎么调用的:
- 用
Db::name()→ 不用手动加,框架加 - 用
Db::table()或裸字符串 → 必须自己写全名(含前缀)或动态获取 - 查模型且模型没设
$table→ 模型不加前缀,和原生 SQL 无关
调试时怎么看真实表名
执行完查询后,立刻调用 Db::getLastSql(),它返回的是最终发送给数据库的 SQL,表名是否带前缀一目了然。这是最可靠的验证方式,比猜配置更直接。

















