平时在查询时,很容易碰到在循环中再查询的情况,因为重复查询表,导致速度变慢。

    以我曾做过的一个项目为例,以下是我没优化前的代码:

    1. <?php
    2. //...
    3. //本次考试下的所有科目
    4. $query = new Query();
    5. $query->select(['es.se_course_id','bc.name'])
    6. ->from(['es'=>SchExamSingle::tableName()])
    7. ->leftJoin(['bc'=>BaseCourse::tableName()],"es.se_course_id=bc.course_id")
    8. ->where(['me_id'=>$me_id]);
    9. $schSingleSubjects = $query->all();
    10. if(empty($schSingleSubjects)){
    11. $this->addError(__METHOD__,'单科考试信息不存在!',ApiConst::RESPONSE_STATUS_ERROR_PARAM);
    12. return false;
    13. }
    14. $subjects = [];
    15. foreach($schSingleSubjects as $subject){
    16. $subjects[$subject['se_course_id']]=$subject['name'];
    17. }
    18. $subQuery = new Query();
    19. $subQuery->select(['mess.me_id','mess.mess_score','mess.mess_order','mess.student_id','mess.student_name','mess.class_id','mess.school_id','mess.school_name','bsc.name AS class_name','bsg.name as grade_name'])
    20. ->from(['mess'=>SchMultiExamStudentScore::tableName()])
    21. ->leftJoin(['bsc'=>BaseSchoolClass::tableName()],"mess.school_id=bsc.school_id AND mess.class_id=bsc.code")
    22. ->leftJoin(['bsg'=>BaseGrade::tableName()],"bsc.grade_id=bsg.grade_id")
    23. ->where(['mess.me_id'=>$me_id,'bsc.grade_id'=>$schExamMulti->me_grade_id]);
    24. //这儿省略了一些权限判断之类的...
    25. $subQuery->orderBy(['mess.mess_score'=>SORT_DESC]);
    26. //$list = $subQuery->all();
    27. $result = [];
    28. foreach($subQuery->each() as $key=>$list){
    29. //foreach ($list as $key=>$val){
    30. $result[$key]['准考证号'] = $list['student_id'];
    31. $result[$key]['姓名'] = $list['student_name'];
    32. $result[$key]['班级'] = $list['grade_name'].$list['class_name'];
    33. $result[$key]['学校'] = $list['school_name'];
    34. foreach ($subjects as $subid => $subname){
    35. $query = new Query();
    36. $query->select(['ess.ess_score'])
    37. ->from(['ess'=>SchSingleExamStudentScore::tableName()])
    38. ->leftJoin(['es'=>SchExamSingle::tableName()],"es.se_id=ess.se_id")
    39. //->leftJoin(['bc'=>BaseCourse::tableName()],"bc.course_id=es.se_course_id")
    40. ->where(['ess.student_id'=>$list['student_id'],'ess.school_id'=>$list['school_id'],'es.se_course_id'=>$subid,'ess.me_id'=>$me_id]);
    41. $score = $query->one();
    42. $result[$key][$subname] = $score['ess_score'];
    43. }
    44. $result[$key]['总分'] = $list['mess_score'];
    45. $result[$key]['联考排名'] = $list['mess_order'];
    46. }
    47. return $result;

    由上代码可见,特别是77-86行间,循环中再查询。整个接口下来最终耗时约14分钟的样子。


    然后今天用了下字典:

    1. <?php
    2. ...
    3. # 获取年级字典 ['grade_id'=>'grade_name', ...]
    4. $gradeDict = (new MGrade())->getGradeNameDict();
    5. # 获取科目字典 ['course_id'=>'course_name', ...]
    6. $courseDict = (new MCourse())->getCourseDict();
    7. # 获取某次考试下的信息字典 ['se_id'=>['se_id','se_course_id','...']]
    8. $singExamDict = $this->getSingExamDict($me_id);
    9. foreach ($singExamDict as $item){
    10. $head[] = $courseDict[$singExamDict[$item['se_id']]['se_course_id']];
    11. }
    12. # 获取某次考试所有学生成绩信息
    13. /**
    14. * ['student_id1'=>
    15. * [
    16. * 'se_id1'=>[...],
    17. * 'se_id2'=>[...],
    18. * 'se_id3'=>[...],
    19. * ...
    20. * ]
    21. * ...
    22. * ]
    23. */
    24. $studentSingExamScoreDict = $this->getStudentSingScoreDict($me_id);
    25. $subQuery = new Query();
    26. $subQuery->select(['mess.me_id','mess.mess_score','mess.mess_order','mess.student_id','mess.student_name','mess.class_id','mess.school_id','mess.school_name','bsc.name AS class_name','bsc.grade_id'])
    27. ->from(['mess'=>SchMultiExamStudentScore::tableName()])
    28. ->leftJoin(['bsc'=>BaseSchoolClass::tableName()],"mess.school_id=bsc.school_id AND mess.class_id=bsc.code")
    29. //->leftJoin(['bsg'=>BaseGrade::tableName()],"bsc.grade_id=bsg.grade_id")
    30. ->where(['mess.me_id'=>$me_id,'bsc.grade_id'=>$schExamMulti->me_grade_id]);
    31. //这儿省略了一些权限判断筛选之类的..
    32. $subQuery->orderBy(['mess.mess_score'=>SORT_DESC]);
    33. //$list = $subQuery->all();
    34. $content = [];
    35. foreach($subQuery->each() as $key=>$list){
    36. //foreach ($list as $key=>$val){
    37. $content[$key][] = $list['student_id'];
    38. $content[$key][] = $list['student_name'];
    39. # 学校
    40. $content[$key][] = $list['school_name'];
    41. # 年级班级
    42. if(array_key_exists($list['grade_id'],$gradeDict)){
    43. $gradeName = $gradeDict[$list['grade_id']];
    44. }else{
    45. $gradeName = '-';
    46. }
    47. $content[$key][] = $gradeName.$list['class_name'];
    48. # 单科成绩
    49. foreach ($singExamDict as $exam){
    50. $se_id = $exam['se_id']; //单科id
    51. $student_id = $list['student_id']; //学生id
    52. //$course_name = $courseDict[$exam['se_course_id']]; //课程名称
    53. if(isset($studentSingExamScoreDict[$student_id][$se_id])){
    54. $content[$key][] = $studentSingExamScoreDict[$student_id][$se_id]['ess_score'];
    55. }else{
    56. $content[$key][] = '-';
    57. }
    58. }
    59. $content[$key][] = $list['mess_score'];
    60. $content[$key][] = $list['mess_order'];
    61. }
    62. $result['head'] = $head;
    63. $result['content'] = $content;
    64. return $result;

    最终下来整个接口只用到4-5分钟的样子,比之前快了3倍,还是很厉害的。
    当然这个速度肯定还是慢了,还需要优化,我再想看看有没有其它好方法再说。


    然后贴一下其中一个字典的代码:

    1. <?php
    2. /**
    3. * 获取学生某次考试的单科考试成绩信息
    4. * @param $me_id
    5. * @return array
    6. */
    7. private function getStudentSingScoreDict($me_id)
    8. {
    9. $dict = [];
    10. $query = new Query();
    11. $query->select([
    12. 'student_id',
    13. 'se_id',
    14. 'ess_score',//总分
    15. 'ess_class_order',//班级排名
    16. 'ess_status',//状态
    17. 'ess_grade_order',//年级排名
    18. 'ess_order',//所有排名
    19. 'ess_province_order',//省排名
    20. 'ess_city_order',//市排名
    21. 'ess_district_order',//地区排名
    22. ]);
    23. $query->from(SchSingleExamStudentScore::tableName());
    24. $query->where(['me_id'=>$me_id]);
    25. $query->orderBy(['se_id'=>SORT_ASC]);
    26. $res = $query->all();
    27. foreach ($res as $item){
    28. $key = $item['student_id'];
    29. $dict[$key][$item['se_id']] = $item;
    30. }
    31. return $dict;
    32. }