Laravel QueryBuilder查询结果去重:解决laravel-report-generator报表重复问题
问题原因
- Laravel的
distinct()方法不支持传入字段参数,作用是对SELECT语句中所有字段的组合结果去重。你当前SELECT同时包含diagnosis和updated_at两个字段,同一种疾病只要更新时间不同就会被判定为不同行,因此去重无效,还可能触发SQL语法错误。 - 原代码在列闭包中重复查询数据库,存在严重的N+1性能问题,且百分比计算逻辑错误:
$result->count()实际是获取当前行的字段数量,并非指定时间段内的总确诊数。
修复方案
直接使用GROUP BY按疾病分组统计,在SQL层面完成去重和计数,避免后续二次查询,修复后的完整代码如下:
$fromDate = $request->fromDate; $toDate = $request->toDate; $sortBy = 'case_count'; // 可按需改为diagnosis或latest_updated_at $title = 'CHATTHE GROUP'; // Report title $subTitle = 'Mobidity Report'; // 提前计算指定时间段总确诊数,仅查询1次 $totalCases = Diagnosis::whereBetween('updated_at', [$fromDate, $toDate])->count(); $meta = [ // For displaying filters description on header ''=> $subTitle, 'From' => $fromDate . ' To: ' . $toDate, 'Generated By' => Auth::user()->name ]; // 按疾病分组统计,天然去重 $queryBuilder = Diagnosis::selectRaw('diagnosis, COUNT(*) as case_count, MAX(updated_at) as latest_updated_at') ->whereBetween('updated_at', [$fromDate, $toDate]) ->groupBy('diagnosis') ->orderBy($sortBy, 'ASC'); $columns = [ // Set Column to be displayed 'DISEASE' => 'diagnosis', 'NO OF CASES' => 'case_count', '% TOTAL' => function($result) use ($totalCases){ // 避免除以0报错,保留2位小数 return $totalCases > 0 ? round(($result->case_count / $totalCases) * 100, 2) : 0; } ]; return ExcelReport::of($title, $meta, $queryBuilder, $columns) ->editColumns(['DISEASE', '% TOTAL'], [ // Mass edit column 'class' => 'right bold' ]) ->showHeader(true) ->simple() ->download('mobidityReport');
核心修改点
- 移除无效的
distinct()调用,改用groupBy('diagnosis)按疾病名称分组,天然实现疾病去重 - 提前计算指定时间段的总确诊数,仅查询一次数据库,解决原代码的N+1性能问题
- 用SQL的
COUNT(*)直接在分组时统计每种疾病的确诊数,无需在列闭包中重复查询 - 修复百分比计算逻辑,新增除以0容错处理
- 保留了
latest_updated_at字段,如果需要按更新时间排序,可将$sortBy改为latest_updated_at
内容的提问来源于stack exchange,提问作者The Only Smart Boy
相关产品推荐
相关产品推荐

