原生 sql 仅在 query builder 无法表达逻辑时使用,典型场景包括:mysql 特有语法(如 on duplicate key update)、复杂嵌套子查询或 union、性能敏感的索引强制与事务控制;必须用参数绑定而非字符串拼接,避免双重转义。

原生 SQL 在 CodeIgniter 中的适用边界
绝大多数场景下,Query Builder(如 select()、where_in()、insert_batch())完全够用,且更安全、可移植、易维护。原生 SQL 不是“高级用法”,而是“不得已的选择”——它只应在 Query Builder 真实无法表达逻辑时才启用。
必须用原生 SQL 的三个典型场景
以下情况若强行用 Query Builder,要么写不出来,要么绕弯子导致可读性崩坏:
- 需要 MySQL 特有语法:比如
ON DUPLICATE KEY UPDATE实现 upsert,INSERT ... SELECT批量导入,或带窗口函数的复杂聚合查询 - 多表关联逻辑超出链式方法覆盖范围:例如嵌套子查询作为字段值(
SELECT *, (SELECT COUNT(*) FROM logs l WHERE l.user_id = u.id) AS log_count),或UNION ALL合并异构结果集 - 性能敏感的定制化优化:如强制指定索引(
FORCE INDEX (idx_status_time))、禁用自动提交做事务批量控制,或调用存储过程
用原生 SQL 时最容易踩的坑
错误不是出在“写不写得对”,而常出在“怎么拼进去”:
- 绝对不要字符串拼接用户输入:
"WHERE name = '" . $this->input->get('name') . "'"是高危操作,哪怕加了mysql_real_escape_string也难保万无一失 -
$this->db->escape()返回的是带单引号的字符串(如'O\'Reilly'),拼进 SQL 时不能再额外加引号,否则变成''O\'Reilly'' - 参数绑定只支持
query(),不支持simple_query();绑定占位符统一用?,顺序必须与数组参数严格一致 - CI 3 中
escape_str()仅用于字符串内容转义,不能替代完整 SQL 参数化;CI 4 已移除该方法,统一用escape()或绑定
安全写法示例:ON DUPLICATE KEY UPDATE
这是批量导入中最常遇到的原生需求,也是最容易出错的点:
$sql = "INSERT INTO users (email, name, status) VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE name = VALUES(name), status = VALUES(status)";
$this->db->query($sql, [$email, $name, $status]);
注意:VALUES(name) 是 MySQL 内置函数,引用本次待插入的值,不是 PHP 变量;所有 ? 占位符由框架统一转义,无需手动处理引号或类型。
真正麻烦的从来不是语法本身,而是混合使用 Query Builder 和原生 SQL 时的转义责任归属——一旦模糊,双重转义或漏转义就紧随而来。











