Laravel Eloquent多表关联查询优化 降低患者档案系统服务负载
优化方案
1. 改用嵌套预加载减少查询次数
Eloquent原生支持嵌套预加载关联,你只需要提前在各模型中定义好关联关系(包括主表到子表、子表到孙表的关联),即可一次性加载所有层级的关联数据,无需分开多次按patient_id查询:
- 先在
Patient模型中定义所有关联:属于关联country/city/recourse,一对多关联appointments/invoices/medicines/diseases/treatments/sessionPats - 再在子表模型中定义各自的关联,比如
Treatment模型中定义属于关联treatment_cat、invoice等
优化后的代码示例:
$patient = Patient::select('id', '你需要的患者基础字段...') ->with([ 'country:id,name', 'city:id,name', 'recourse:id,name', 'appointments' => function ($q) { $q->select('id', 'patient_id', 'branch_id', 'doctor_id', 'service_cat_id', '其他预约字段') ->orderBy('id', 'asc') ->with(['branch:id,name', 'doctor:id,first_name', 'service_cat:id,name']); }, 'invoices' => function ($q) { $q->select('id', 'patient_id', 'creditor_cat_id', 'final_price', 'status') ->with('service_cat:id,name'); }, 'medicines' => function ($q) { $q->select('id', 'patient_id', 'medicines_cats', 'start', 'end', 'status') ->orderBy('status', 'asc') ->with('medicinescats:id,name'); }, 'diseases' => function ($q) { $q->select('id', 'patient_id', 'disease_cats', 'start', 'end', 'status') ->orderBy('status', 'asc') ->with('diseasecats:id,name'); }, 'treatments' => function ($q) { $q->select('id', 'patient_id', 'treatment_cat_id', 'sessions', 'sessions_done', 'start', 'end', 'status') ->orderBy('status', 'asc') ->with('treatment_cat:id,name'); }, 'sessionPats' => function ($q) { $q->select('id', 'patient_id', 'services_cat_id', 'treatment_id', 'invoice_id', 'status') ->orderBy('status', 'asc') ->with([ 'service_cat:id,name', 'treatment:id,sessions', 'invoice:id,code,status' ]); } ]) ->find($id); // 字典类数据查询保持不变,可额外加缓存优化 $medicine_cat = Medicine_cat::all(['id', 'name']); $disease_cat = Disease_cat::all(['id', 'name']); $treatment_cat = Treatment_cat::all(['id', 'name']); $service_cat_treat = Service_cat::where('type', 2)->get(['id', 'name']);
2. 额外性能优化点
- 所有查询必须显式指定字段,尤其是关联所需的外键字段(比如查询
appointments时必须选patient_id/branch_id等外键),避免关联失效和多余字段的IO开销 - 业务表的
patient_id、排序字段建议加索引,比如可添加联合索引index(patient_id, id)、index(patient_id, status),大幅提升过滤、排序速度 - 更新频率低的字典类数据(所有
*_cat表)可加缓存,比如用Laravel内置缓存缓存数小时,避免每次请求都查全表 - 若不需要一次性返回所有关联数据,可按业务模块拆分接口,比如基础信息、预约、用药、治疗分接口查询,降低单次请求的数据库压力
优化效果
原写法累计需要执行至少18次SQL查询,优化后仅需13次左右,避免了重复的patient_id过滤查询,数据库负载和查询耗时都会明显下降。
内容的提问来源于stack exchange,提问作者user17171534
相关产品推荐
相关产品推荐

