Laravel 8 Query Builder统计关联合同数量返回数据不全如何解决?
问题原因
- 你当前使用Query Builder时默认调用的
join是INNER JOIN逻辑,关联contracts表时会自动过滤掉没有关联合同的用户,这就是你总统计数只有80条、缺失23条无合同用户的核心原因。 - 额外需要注意的是,你原代码未选择
roles表的字段,无法拿到用户对应的角色信息;同时如果MySQL开启了ONLY_FULL_GROUP_BY模式(Laravel 8默认配置开启),你当前groupBy只写users.id会触发语法报错。
修复后的Query Builder写法
写法1:LEFT JOIN关联实现
DB::table('users') // 改用LEFT JOIN关联合同表,保留无合同的用户记录 ->leftJoin('contracts', 'users.id', '=', 'contracts.user_id') // 如果存在用户role_id为空/无对应角色的情况,这里也改用leftJoin ->join('roles', 'roles.id', '=', 'users.role_id') ->select( 'users.id', 'users.username', 'users.email', 'roles.name as role_name', DB::raw('COUNT(contracts.id) as numberOfContracts') ) // 适配ONLY_FULL_GROUP_BY模式,所有非聚合字段都加入groupBy ->groupBy('users.id', 'users.username', 'users.email', 'roles.name') ->paginate(10);
写法2:子查询实现(兼容性、性能更优)
这种写法和你用EloquentwithCount的底层逻辑一致,不需要对主表多字段做groupBy,避免后续加字段还要修改groupBy的问题:
// 先构造合同计数子查询 $contractCountQuery = DB::table('contracts') ->select(DB::raw('COUNT(id) as numberOfContracts, user_id')) ->groupBy('user_id'); // 主查询关联子查询拿计数 DB::table('users') ->join('roles', 'users.role_id', '=', 'roles.id') ->leftJoinSub($contractCountQuery, 'contract_counts', 'users.id', '=', 'contract_counts.user_id') ->select( 'users.id', 'users.username', 'users.email', 'roles.name as role_name', // 无合同的用户计数默认填充0 DB::raw('IFNULL(contract_counts.numberOfContracts, 0) as numberOfContracts') ) ->paginate(10);
与Eloquent写法的差异说明
你用到的EloquentwithCount本质是用子查询做关联计数,不会过滤主表users的任何记录,所以能返回全部103条用户数据,上面的写法2就是Query Builder层面还原了Eloquent的实现逻辑。
内容的提问来源于stack exchange,提问作者calin24
相关产品推荐
相关产品推荐

