Db::query()专用于SELECT等只读语句,返回二维数组;Db::execute()专用于INSERT/UPDATE等写操作,返回影响行数;二者语义严格区分,混用将导致数据未查出、未更新或静默失败。

Db::query() 和 Db::execute() 必须严格按语义区分使用,混用会导致数据没查出来、改不成功、静默失败,且读写分离下会走错库。
查数据必须用 Db::query(),别拿它执行 UPDATE
它只处理有结果集的语句:SELECT、SHOW、EXPLAIN、WITH。返回二维数组(空结果也是 []),失败返回 false。
- 错误写法:
Db::query("UPDATE user SET status = 1 WHERE id = ?", [123])—— 不报错,但 SQL 根本没进写库,数据不变 - 正确写法:
Db::query("SELECT * FROM user WHERE status = :status", ['status' => 1]),命名占位符更清晰 - TP8 支持问号绑定,但
?顺序必须和数组值严格对应;命名绑定(:param)推荐优先使用 -
Db::getLastSql()只记录最近一次调用,调试时要确保它紧跟在query()或execute()后面
改数据必须用 Db::execute(),别拿它查列表
它专用于无结果集的写操作:INSERT、UPDATE、DELETE、REPLACE、TRUNCATE。返回影响行数(int),失败也返回 false。
- 错误写法:
$list = Db::execute("SELECT * FROM user"); foreach($list as ...)——$list是数字(比如 0 或 1),直接 foreach 报错 - 正确写法:
$affected = Db::execute("UPDATE user SET name = ? WHERE id = ?", [$name, $id]); if ($affected === false) { /* 处理失败 */ } - 要获取最后插入 ID,不能靠
execute()返回值,得额外调Db::getLastInsID() - 批量插入建议用
Db::name('user')->insertAll($data),比手写原生 INSERT 更稳,也避免 SQL 长度超限
动态字段/表名不能绑定,必须白名单校验后拼接
:table 或 :order_field 这类占位符不会被替换,PDO 会当成字符串字面量或直接报语法错误。
立即学习“PHP免费学习笔记(深入)”;
- 错误写法:
Db::query("SELECT * FROM :table WHERE id = ?", [$tableName, $id]) - 正确做法:先校验
$sortField是否在['id', 'name', 'create_time']白名单中,再拼:"ORDER BY {$sortField} DESC" - 表前缀可用
__USER__,框架自动替换,但仅限静态写法;变量拼接场景下仍需白名单+手动拼接 - LIKE 模糊查询通配符要包在绑定值里:
['name' => "%{$keyword}%"],别写进 SQL 字符串
MySQL 8.0+ 下原生 SQL 报错?大概率是 PDO 预处理没开模拟
TP8 默认未设置 PDO::ATTR_EMULATE_PREPARES => true,而 MySQL 8.0+ 对原生预处理更严格,容易触发 SQLSTATE[HY000]: General error: 2034。
- 解决方式:在
config/database.php的 MySQL 连接配置中,显式添加:'options' => [PDO::ATTR_EMULATE_PREPARES => true] - DDL 语句(如 CREATE TEMPORARY TABLE、ALTER TABLE)本身不支持事务回滚,禁止在
Db::startTrans()块中执行 - 事务中所有原生操作都必须包裹在
try-catch里,捕获think\db\exception\DataNotFoundException和底层 PDO 异常 - 哪怕只是
Db::query(),只要没抛异常,框架就认为“成功”,不会中断事务——回滚与否完全取决于你是否主动throw或catch
真正容易被忽略的是:事务边界和语句兼容性不是靠框架自动兜底的。一个 SET @var = 1 或 DROP TABLE 写进去,rollback() 就形同虚设。



















