ThinkPHP支持查询构造器与原生SQL安全混合使用:可在where、field中用raw()嵌入原生表达式;用query()执行复杂原生查询并转模型;join/union中引用原生子查询;须全程参数绑定防注入,校验标识符,统一数据库连接。

ThinkPHP 的查询构造器和原生 SQL 可以安全、灵活地混合使用,关键在于明确各自适用场景,并通过正确方式衔接,避免 SQL 注入或语法错误。
在查询构造器中嵌入原生 SQL 片段
适用于 WHERE 条件、字段别名、函数计算等需数据库特有语法的场景。使用 raw() 或直接传入字符串(配合参数绑定):
- 用 raw() 包裹原生表达式,自动识别为原始 SQL,不转义:
$query->where('age', '>', raw('TIMESTAMPDIFF(YEAR, birth_date, NOW())')); - 在 field() 中混用函数:
$query->field('id, name, CONCAT(first_name, " ", last_name) AS full_name'); - WHERE 中含子查询时,用字符串 + 参数绑定更清晰:
$query->where('status', 'IN', '(SELECT status FROM think_order_status WHERE active = ?)', [1]);
用 query() 方法执行原生 SELECT 并与构造器结果合并
适合复杂多表关联、窗口函数、CTE 等构造器难以表达的查询。返回结果为数组,可与构造器结果统一处理:
- 执行带参数的原生查询(自动预处理防注入):
$result = Db::query('SELECT u.name, COUNT(o.id) as order_count FROM think_user u LEFT JOIN think_order o ON u.id = o.user_id WHERE u.level > ? GROUP BY u.id', [5]); - 若需复用模型逻辑(如自动转换、事件),可将原生结果转为模型集合:
$collection = collection($result)->map(function ($item) { return new User($item); });
在 join()、union() 等链式方法中引用原生表或子查询
构造器支持将原生 SQL 作为临时表参与关联或合并:
立即学习“PHP免费学习笔记(深入)”;
- JOIN 子查询(注意加括号和别名):
$query->join('(SELECT user_id, MAX(create_time) as last_login FROM think_login_log GROUP BY user_id) ll', 'u.id = ll.user_id', 'LEFT'); - UNION 合并两个构造器查询(需字段数、类型一致):
$a = Db::table('think_user')->field('id,name')->where('status', 1);
$b = Db::table('think_admin')->field('id,name')->where('is_active', 1);
$result = $a->union($b)->select();
注意事项与避坑点
混合使用不是随意拼接,需守住安全与可维护底线:
- 所有用户输入必须通过参数绑定(? 占位符或键名绑定),禁用字符串拼接;
- 原生 SQL 中的表名、字段名若来自变量,需手动校验白名单或使用 parseSql() 过滤;
- 事务中混合执行时,确保全部走同一连接(Db::connect() 显式指定);
- 调试阶段开启 SQL 日志(show_sql => true),确认最终生成语句符合预期。



















