ThinkPHP 6.0执行原生SQL须严格区分:查数据用Db::query()返回二维数组,适用于SELECT等;改数据用Db::execute()返回影响行数,适用于INSERT/UPDATE/DELETE;混用易致逻辑错误或线上事故。

ThinkPHP 6.0 执行原生 SQL 必须严格区分用途:查数据用 Db::query(),改数据用 Db::execute()。混用会导致返回值误导、逻辑错误甚至线上事故。
查数据:用 Db::query() 获取结果数组
适用于 SELECT、SHOW、EXPLAIN 等能返回记录集的语句,返回二维数组,可直接遍历或传给模板。
- 支持问号占位符(推荐):
Db::query('SELECT * FROM user WHERE status = ? AND id > ?', [1, 100]) - 支持命名占位符(语义清晰):
Db::query('SELECT id,name FROM user WHERE role = :role', ['role' => 'admin']) - LIKE 模糊匹配需手动拼通配符:
['%' . $kw . '%'],不能写成?% - 读写分离下默认走从库;如需主库查询,加第三个参数
true:Db::query($sql, $params, true)
改数据:用 Db::execute() 获取影响行数
专用于 INSERT、UPDATE、DELETE、TRUNCATE 等写操作,返回整数(成功为影响行数,失败为 false),不返回数据本身。
- 单条更新:
Db::execute('UPDATE user SET last_login = NOW() WHERE id = ?', [123]) - 批量插入(注意参数顺序一一对应):
Db::execute('INSERT INTO log (uid, action, ip) VALUES (?, ?, ?), (?, ?, ?)', [1,'login','192.168.1.1', 2,'logout','192.168.1.2']) - 获取刚插入 ID 需额外调:
Db::getLastInsID(),execute本身不提供 - 误用
query执行 DELETE 是典型翻车点:语句执行了,但返回空数组[],容易误判为“没删成”
安全绑定:值可用占位符,表名字段名必须白名单校验
占位符只防御「值」注入,对表名、字段名、ORDER BY、GROUP BY 等动态部分完全无效,必须提前校验。
立即学习“PHP免费学习笔记(深入)”;
- 错误示范:
ORDER BY ?→ 会被当字符串字面量,排序失效 - 正确做法:先白名单过滤再拼接,例如:
$allowedSorts = ['id','name','create_time']; if (!in_array($sortField, $allowedSorts)) { throw new \Exception('非法排序字段'); } - 表名占位
{:table}或__TABLE__仍是字符串替换,仍需校验:if (!in_array($table, ['user', 'order', 'log'])) { ... }
子查询和复杂 SQL 要手动写全
query() 和 execute() 只做字符串透传,不解析嵌套结构。子查询必须手写完整 SQL,括号、逗号、别名都要自己保证正确。
- 可行示例:
Db::query('SELECT * FROM (SELECT id, name FROM user WHERE status = 1) AS t WHERE t.id > 10') - 参数绑定需格外小心:子查询中的占位符属于整个 SQL 字符串,要统一按顺序或命名传参
- 如需动态构建子查询,建议优先使用查询构造器的
selectSub()方法,再转为原生 SQL



















