CodeIgniter中LEFT JOIN多表查询需用join()方法指定'left'类型,确保主表全量返回、右表无匹配则补NULL;推荐子查询获取用户最新订单再关联,避免limit(1)失效问题,并注意别名、索引及NULL值处理。

CodeIgniter 中使用 LEFT JOIN 多表查询,核心是借助 Query Builder(即 Active Record)的 join() 方法,并明确指定连接类型为 'left'。它和原生 SQL 的 LEFT JOIN 逻辑一致:保证主表(FROM 后的表)所有记录返回,右表无匹配则补 NULL。下面是一个贴近真实业务的完整案例,包含建表逻辑、模型写法、控制器调用和注意事项。
一、数据库表结构与关联关系
假设我们有三张表:
- users(用户表):id, username, email, created_at
- orders(订单表):id, user_id, amount, status, created_at
- order_items(订单明细表):id, order_id, product_name, quantity, price
关系:users.id → orders.user_id → order_items.order_id。我们要查出每个用户信息,以及他们最新一笔订单的总金额和商品数(即使没下单也显示)。
二、Model 层写法(推荐使用 Query Builder)
在 app/Models/UserModel.php 中:
public function getUsersWithLatestOrder()
{
return $this->select('u.id, u.username, u.email,
o.id as order_id, o.amount, o.created_at as order_date,
COUNT(oi.id) as item_count')
->from('users u')
->join('orders o', 'u.id = o.user_id', 'left')
->join('order_items oi', 'o.id = oi.order_id', 'left')
->groupStart()
->groupBy('u.id')
->groupBy('o.id')
->groupEnd()
->orderBy('o.created_at', 'desc')
->limit(1)
->get()
->getResult();
}
⚠️ 注意:上面写法在 CodeIgniter 4.4+ 中会因 limit(1) 作用于整个结果集而失效(无法按用户取“最新订单”)。更稳妥的做法是用子查询或分步处理——但实际中建议改用以下优化写法:
public function getUsersWithLatestOrder()
{
// 先查每个用户的最新订单 ID(子查询)
$subquery = "(SELECT user_id, MAX(created_at) as max_date FROM orders GROUP BY user_id) latest_orders";
return $this->select('u.id, u.username, u.email,
o.id as order_id, o.amount, o.created_at as order_date,
(SELECT COUNT(*) FROM order_items WHERE order_id = o.id) as item_count')
->from('users u')
->join($subquery, 'u.id = latest_orders.user_id', 'left')
->join('orders o', 'u.id = o.user_id AND o.created_at = latest_orders.max_date', 'left')
->get()
->getResult();
}
三、Controller 调用与输出
在 app/Controllers/UserController.php 中:
public function index()
{
$model = new UserModel();
$data['users'] = $model->getUsersWithLatestOrder();
return view('user/list', $data);
}
视图中可安全使用:
<?php foreach ($users as $u): ?>
<tr>
<td><?= esc($u->username) ?></td>
<td><?= $u->amount ?: '未下单' ?></td>
<td><?= $u->item_count ?: 0 ?></td>
</tr>
<?php endforeach ?>
四、关键细节与避坑提醒
-
别名必须唯一:多个 LEFT JOIN 时,每张表都要起明确别名(如
u、o、oi),避免字段冲突 -
ON 条件要写全:特别是多表 LEFT JOIN 时,第二个 JOIN 的条件需同时关联前一张表和当前表,例如
'o.id = oi.order_id',不能漏掉o.前缀 -
NULL 值要判空:LEFT JOIN 后的字段可能为 NULL,PHP 中用
?:或isset()处理,避免 Notice -
性能优先加索引:确保
orders.user_id、order_items.order_id上有索引,否则大数据量下会明显变慢 - 慎用 SELECT *:多表 LEFT JOIN 时字段重名易出错,务必显式列出所需字段并加别名
不复杂但容易忽略。

















