Query Builder最适合单表查询和简单双表关联,如用户列表关联角色名;慎用于三表以上复杂关联或含函数的ON条件;聚合统计需严格匹配分组规则;不支持正则、全文检索等高级匹配;复杂查询建议直接用安全的原生SQL。

适合单表条件查询和简单多表关联
Query Builder 最自然的使用场景是单表的增删改查,比如按 status、user_id 或时间范围筛选记录。它能安全生成 WHERE、ORDER BY、LIMIT 等子句,且自动转义变量,避免 SQL 注入。
多表关联也支持,但仅限基础 JOIN(如 join('orders o', 'o.user_id = u.id')),不处理嵌套 JOIN 或复杂别名依赖。一旦关联逻辑涉及三张以上表、或需要 ON 条件里带函数(如 DATE(o.created_at)),就容易写错或难以调试。
- ✅ 推荐:用户列表 + 关联角色名(两张表,主外键清晰)
- ⚠️ 谨慎:订单 + 商品 + 库存 + 供应商四表联查,且含状态聚合
- ❌ 避免:用 Query Builder 拼
LEFT JOIN (...) AS sub子查询
适合聚合统计但需注意字段一致性
做 COUNT、SUM、GROUP BY 是可行的,但必须严格匹配数据库的分组规则。例如 MySQL 开启 ONLY_FULL_GROUP_BY 时,SELECT user_id, name, COUNT(*) 必须把 name 也放进 group_by('user_id, name'),否则报错。
CI 不会帮你校验这个逻辑,错误只在执行时暴露,且提示模糊(常是“Invalid use of group function”之类)。
-
count_all_results()适合带where()的总数统计,比get()->num_rows()更省资源 -
select('SUM(amount) as total')必须配get()->row()->total ?? 0,空结果时直接取属性会报 Notice - 别用
distinct()替代group_by()去重——它只对整行生效,字段重复但其他列不同就会漏去重
不适合复杂字符串处理和高级模式匹配
Query Builder 的 like() 只覆盖基础前缀/后缀/全模糊('after'、'before'、'both'),没法表达正则、全文索引、或大小写敏感匹配。比如想查 “以 A 或 B 开头”,就得拆成两个 or_like() 并包 group_start(),代码冗长且易漏括号。
更麻烦的是,像 WHERE title REGEXP '^[A-C]+' 或 MATCH(title) AGAINST('php framework' IN NATURAL LANGUAGE MODE) 这类能力,Query Builder 根本不提供封装,硬写就得退到原生 query()。
- 不要试图用
like('field', '%value%')手动拼通配符——like()默认加%,重复加会导致%%value%%,查不到数据 -
where_in()看似方便,但传空数组[]会生成IN (),MySQL 直接返回空集,得自己if (!empty($arr))包一层 - 多个
or_where_in()混用where()时,优先级靠group_start()控制,漏掉就逻辑错乱
真正复杂的查询建议直接用原生 SQL
当需求出现窗口函数、CTE、递归查询、跨库 JOIN、或需要精细控制执行计划时,Query Builder 不是省事,而是添堵。CI 的 query() 完全支持参数绑定:$this->db->query("SELECT * FROM logs WHERE created_at > ?", [$date]),既安全又自由。
关键不是“能不能”,而是“值不值得”。一个用了 5 层 group_start() 和 3 个 or_having() 的 Query Builder 链式调用,远不如一段缩进清晰的原生 SQL 易读、易测、易改。
容易被忽略的一点:Query Builder 生成的 SQL 不会走查询缓存(除非你手动开启并配置),而原生语句配合 CI 的 cache_on 设置反而更容易命中缓存。


















