Laravel中如何统计全年12个月的含体检服务就诊次数及总就诊次数?现有查询方案性能不佳求优化
优化Laravel全年就诊数据统计的方案
嘿,我来帮你解决这个性能问题!你的当前代码循环12次查询数据库,这就是耗时的根源——频繁的数据库往返开销太大了。咱们可以把12次查询合并成1次,一次性拿到全年12个月的统计数据,效率会高很多,同时还能补上总就诊次数的统计。
核心优化思路
通过一次数据库查询+按月份分组统计,替代循环多次查询,减少数据库交互次数;同时利用原生SQL函数完成两类数据的统计,再处理空月份的补全逻辑。
优化方案一:使用Eloquent结合原生SQL统计
这个方案用EXISTS子查询判断就诊是否关联体检服务,同时统计总次数和体检就诊次数:
$clinic = $user->Clinics->first(); $currentYear = date('Y'); // 一次性拉取全年所有有数据的月份统计 $monthlyStats = Visit::where('clinic_id', $clinic->id) ->whereYear('created_at', $currentYear) ->select([ DB::raw('MONTH(created_at) as month'), DB::raw('COUNT(*) as total_visits'), // 当月总就诊次数 DB::raw('SUM(CASE WHEN EXISTS (SELECT 1 FROM visit_check_ups WHERE visit_check_ups.visit_id = visits.id) THEN 1 ELSE 0 END) as checkup_visits') // 含体检的就诊次数 ]) ->groupBy('month') ->orderBy('month') ->get() ->keyBy('month'); // 初始化全年12个月的数组,确保没有数据的月份也显示0 $userPerMonth = []; for ($i = 1; $i <= 12; $i++) { $stats = $monthlyStats->get($i, (object)['total_visits' => 0, 'checkup_visits' => 0]); $userPerMonth[$i] = [ 'total' => $stats->total_visits, 'checkups' => $stats->checkup_visits ]; } dump($userPerMonth);
优化方案二:使用Left Join统计
如果Visit和VisitCheckUps是一对多关联,也可以用leftJoin来实现,注意要加DISTINCT避免重复计数:
$monthlyStats = Visit::leftJoin('visit_check_ups', 'visits.id', '=', 'visit_check_ups.visit_id') ->where('clinic_id', $clinic->id) ->whereYear('created_at', $currentYear) ->select([ DB::raw('MONTH(visits.created_at) as month'), DB::raw('COUNT(DISTINCT visits.id) as total_visits'), // 去重统计总就诊数 DB::raw('COUNT(DISTINCT visit_check_ups.visit_id) as checkup_visits') // 统计关联体检的就诊数 ]) ->groupBy('month') ->orderBy('month') ->get() ->keyBy('month'); // 同样初始化12个月数据(和方案一一致) $userPerMonth = []; for ($i = 1; $i <= 12; $i++) { $stats = $monthlyStats->get($i, (object)['total_visits' => 0, 'checkup_visits' => 0]); $userPerMonth[$i] = [ 'total' => $stats->total_visits, 'checkups' => $stats->checkup_visits ]; } dump($userPerMonth);
额外性能优化:添加数据库索引
为了让查询更快,建议给相关字段添加索引,大幅提升过滤和关联查询的效率:
-- 给visits表添加clinic_id和created_at的联合索引 ALTER TABLE visits ADD INDEX idx_clinic_created (clinic_id, created_at); -- 给visit_check_ups表添加visit_id的索引 ALTER TABLE visit_check_ups ADD INDEX idx_visit_id (visit_id);
为什么这个方案更快?
原来的代码执行了12次数据库查询,每次都要建立连接、执行SQL、返回结果;优化后只需要1次查询,所有统计逻辑在数据库端完成,减少了90%以上的数据库交互开销,数据量越大,性能提升越明显。
内容的提问来源于stack exchange,提问作者Mostafa Mahmoud
相关产品推荐
相关产品推荐

