
本文介绍在 codeigniter 中替代多表 left join 的高效方案,通过单次子查询 + 条件聚合重构原查询,显著减少 i/o 开销与锁竞争,并配合针对性索引提升执行效率。
本文介绍在 codeigniter 中替代多表 left join 的高效方案,通过单次子查询 + 条件聚合重构原查询,显著减少 i/o 开销与锁竞争,并配合针对性索引提升执行效率。
在实际开发中,为同一主表(如 custome_module)对同一附表(如 restaurant_logs)按不同条件(ntype = 1, 2, 6, 5)进行四次 LEFT JOIN,不仅导致 SQL 执行计划复杂、笛卡尔积风险上升,还会大幅增加内存占用与磁盘 I/O,尤其在 restaurant_logs 数据量较大时性能急剧下降。
更优解是用一次 JOIN 配合条件聚合(Conditional Aggregation)替代多次 JOIN。核心思路:将原本分散在 l1, l2, l3, l4 四个别名中的逻辑,统一到单个 restaurant_logs 表的关联中,再通过 CASE WHEN 分别提取各类型最小值:
$this->db->select("
cm.id,
cm.name,
MIN(CASE WHEN rl.ntype = 1 THEN rl.nmark_completed END) AS call_waiter,
MIN(CASE WHEN rl.ntype = 2 THEN rl.nmark_completed END) AS bill,
MIN(CASE WHEN rl.ntype = 6 THEN rl.nmark_completed END) AS tray,
MIN(CASE WHEN rl.ntype = 5 THEN rl.nmark_completed END) AS ordera
");
$this->db->from('custome_module cm');
$this->db->join('restaurant_logs rl', 'cm.id = rl.nmodule_id', 'left');
$this->db->where('cm.nbranch_id', $this->data['user_session']['nid']);
$this->db->group_by('cm.id, cm.name'); // 显式包含 SELECT 中的非聚合字段(兼容 SQL mode: ONLY_FULL_GROUP_BY)
$query = $this->db->get();✅ 优势说明:
- JOIN 次数从 4 次降至 1 次,避免重复扫描 restaurant_logs 表;
- 利用 CASE WHEN 实现行级条件分流,语义清晰且数据库优化器更易生成高效执行计划;
- 减少临时表创建与中间结果集膨胀,降低内存压力。
⚠️ 关键配套优化(不可忽略):
必须为 restaurant_logs 表建立复合索引,覆盖查询高频过滤与连接字段:
CREATE INDEX idx_logs_module_type ON restaurant_logs (nmodule_id, ntype);
该索引可同时加速 JOIN 条件(nmodule_id)与 WHERE 分支(ntype),使每次聚合查找趋近于 O(log n),而非全表扫描。
? 额外建议:
- 若 nmark_completed 允许为 NULL,MIN() 会自动忽略 NULL 值,行为符合预期;
- 如需进一步提升响应速度且数据变更不频繁,可考虑物化汇总视图或应用层缓存(如 Redis 缓存分支 ID 对应的聚合结果);
- 在 CodeIgniter 4 中,推荐使用 Query Builder 的 selectSum() 或原生 raw 表达式增强可读性,但底层逻辑不变。
综上,摒弃“为每种类型建一个 JOIN”的惯性思维,转向“单次关联 + 条件聚合”范式,辅以精准索引,是优化此类 EAV 风格查询最直接、最有效的工程实践。



















