本文目录导读:

在ThinkPHP中,子查询和闭包是处理复杂查询的两种重要方式,下面详细介绍它们的使用方法。
子查询
使用闭包构建子查询
// 基础子查询
$subQuery = Db::table('orders')
->field('user_id, SUM(amount) as total')
->group('user_id')
->buildSql();
$users = Db::table('users')
->alias('u')
->join($subQuery . ' o', 'u.id = o.user_id')
->select();
where中使用子查询
// IN子查询
$subQuery = Db::table('order')
->where('status', 1)
->field('user_id')
->buildSql();
$users = Db::name('user')
->where('id', 'IN', $subQuery)
->select();
// 使用闭包方式
$users = Db::name('user')
->where(function ($query) {
$query->where('id', 'IN', function ($query) {
$query->table('order')->where('status', 1)->field('user_id');
});
})
->select();
EXISTS子查询
// 使用闭包实现EXISTS
$users = Db::name('user')
->alias('u')
->whereExists(function ($query) {
$query->table('order')
->alias('o')
->where('o.user_id = u.id')
->where('o.amount', '>', 100);
})
->select();
// NOT EXISTS
$users = Db::name('user')
->alias('u')
->whereNotExists(function ($query) {
$query->table('order')
->alias('o')
->where('o.user_id = u.id');
})
->select();
闭包的高级用法
条件分组
// 复杂条件分组
$list = Db::name('user')
->where(function ($query) {
$query->where('status', 1)
->whereOr(function ($query) {
$query->where('score', '>', 80)
->where('level', '>=', 3);
});
})
->select();
动态条件构建
public function search($params)
{
$query = Db::name('user');
// 使用闭包动态添加条件
$query->where(function ($query) use ($params) {
// 关键词搜索
if (!empty($params['keyword'])) {
$query->where('name|email', 'like', "%{$params['keyword']}%");
}
// 状态筛选
if (isset($params['status'])) {
$query->where('status', $params['status']);
}
// 日期范围
if (!empty($params['start_date']) && !empty($params['end_date'])) {
$query->whereBetween('create_time', [
strtotime($params['start_date']),
strtotime($params['end_date'])
]);
}
});
return $query->paginate(15);
}
关联子查询
// 使用关联查询
$users = Db::name('user')
->alias('u')
->field('u.*, o.total_amount')
->join([
'SELECT user_id, SUM(amount) as total_amount FROM orders GROUP BY user_id' => 'o'
], 'o.user_id = u.id')
->select();
// 或者使用闭包
$users = Db::name('user')
->alias('u')
->join(function ($query) {
$query->table('orders')
->alias('o')
->where('o.user_id = u.id');
}, 'u.id = o.user_id')
->select();
实际应用示例
获取每个分类的最新商品
$sql = Db::table('products')
->field('id, category_id, name, price')
->order('create_time desc')
->buildSql();
$list = Db::table($sql . ' p')
->field('p.*')
->group('p.category_id')
->select();
用户消费排行
// 子查询获取用户消费总额
$subQuery = Db::table('orders')
->field('user_id, SUM(total_amount) as total_spent')
->where('status', 'paid')
->group('user_id')
->buildSql();
$rankList = Db::table('users')
->alias('u')
->join($subQuery . ' us', 'u.id = us.user_id')
->field('u.username, us.total_spent')
->order('us.total_spent desc')
->limit(10)
->select();
筛选有订单的用户
// 方式一:使用EXISTS
$users = Db::name('user')
->alias('u')
->whereExists(function ($query) {
$query->table('orders')
->where('user_id = u.id')
->where('status', 1);
})
->select();
// 方式二:使用IN
$users = Db::name('user')
->where('id', 'IN', function ($query) {
$query->table('orders')
->where('status', 1)
->field('user_id');
})
->select();
复杂报表统计
// 统计每个用户的订单和总金额
$list = Db::name('user')
->alias('u')
->field([
'u.id',
'u.username',
'COUNT(o.id)' => 'order_count',
'IFNULL(SUM(o.amount), 0)' => 'total_amount'
])
->join('orders o', 'o.user_id = u.id', 'LEFT')
->group('u.id')
->having('order_count > 0')
->select();
性能优化建议
使用buildSql避免重复查询
// 构建一次,多处使用
$subQuery = Db::name('orders')
->field('user_id, SUM(amount) as total')
->where('status', 1)
->group('user_id')
->buildSql();
// 多个查询中使用
$topUsers = Db::name('user')
->alias('u')
->join($subQuery . ' o', 'u.id = o.user_id')
->order('o.total desc')
->limit(10)
->select();
$userStats = Db::name('user')
->alias('u')
->join($subQuery . ' o', 'u.id = o.user_id')
->field('u.*, o.total')
->select();
使用索引优化
// 确保子查询的关联字段有索引
Db::name('user')
->where('id', 'IN', function ($query) {
$query->table('orders')
->where('status', 1)
->field('user_id');
})
->select();
// 注意:orders表的user_id和status字段应该建立复合索引
避免复杂嵌套
// 不推荐 - 过度嵌套
$result = Db::name('a')
->where('id', 'IN', function ($query) {
$query->table('b')
->where('cid', 'IN', function ($query) {
$query->table('c')->where('status', 1)->field('id');
})
->field('aid');
})
->select();
// 推荐 - 拆分为多个查询
$cids = Db::name('c')->where('status', 1)->column('id');
$aids = Db::name('b')->whereIn('cid', $cids)->column('aid');
$result = Db::name('a')->whereIn('id', $aids)->select();
子查询和闭包是处理复杂业务逻辑的重要工具,合理使用可以大大简化代码,但也要注意性能优化,避免过度复杂的嵌套查询。