Laravel非Eloquent实现多关联计数查询 解决统计值乘积错误问题
问题原因
你当前的写法问题核心是多表JOIN产生了笛卡尔积:当同时JOINaddresses、cars两张关联表时,每个用户的每条地址记录会和该用户所有车辆记录匹配生成组合行,最终单用户的总数据行数等于「地址数量 × 车辆数量」,直接count两个关联表的id自然会得到完全相同、且等于乘积的错误结果。
解决方案
Eloquent的withCount底层实际是用子查询单独统计每个关联的数量,不会联表产生笛卡尔积,你可以用两种方式复现相同逻辑:
方式1:直接用select子查询(写法最简洁,和withCount行为完全一致)
$users = \DB::table('users') ->select([ 'id', 'name', \DB::raw('(SELECT COUNT(*) FROM addresses WHERE addresses.user_id = users.id) as addresses_count'), \DB::raw('(SELECT COUNT(*) FROM cars WHERE cars.user_id = users.id) as cars_count') ]) ->get();
方式2:先单独统计关联表数据再JOIN(适合大表统计场景)
先分别对两张关联表做分组统计,再和用户表JOIN,避免笛卡尔积:
// 先统计每个用户的地址数量 $addressStats = \DB::table('addresses') ->selectRaw('user_id, COUNT(id) as addresses_count') ->groupBy('user_id'); // 先统计每个用户的车辆数量 $carStats = \DB::table('cars') ->selectRaw('user_id, COUNT(id) as cars_count') ->groupBy('user_id'); // 关联统计结果和用户表 $users = \DB::table('users') ->leftJoinSub($addressStats, 'address_stats', 'users.id', '=', 'address_stats.user_id') ->leftJoinSub($carStats, 'car_stats', 'users.id', '=', 'car_stats.user_id') ->select( 'users.id', 'users.name', \DB::raw('IFNULL(addresses_count, 0) as addresses_count'), \DB::raw('IFNULL(cars_count, 0) as cars_count') ) ->get();
两种写法都兼容没有关联数据的用户,统计结果和原EloquentwithCount完全一致。
内容的提问来源于stack exchange,提问作者LoveCoding
相关产品推荐
相关产品推荐

