平时在查询时,很容易碰到在循环中再查询的情况,因为重复查询表,导致速度变慢。
以我曾做过的一个项目为例,以下是我没优化前的代码:
<?php//...//本次考试下的所有科目$query = new Query();$query->select(['es.se_course_id','bc.name'])->from(['es'=>SchExamSingle::tableName()])->leftJoin(['bc'=>BaseCourse::tableName()],"es.se_course_id=bc.course_id")->where(['me_id'=>$me_id]);$schSingleSubjects = $query->all();if(empty($schSingleSubjects)){$this->addError(__METHOD__,'单科考试信息不存在!',ApiConst::RESPONSE_STATUS_ERROR_PARAM);return false;}$subjects = [];foreach($schSingleSubjects as $subject){$subjects[$subject['se_course_id']]=$subject['name'];}$subQuery = new Query();$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'])->from(['mess'=>SchMultiExamStudentScore::tableName()])->leftJoin(['bsc'=>BaseSchoolClass::tableName()],"mess.school_id=bsc.school_id AND mess.class_id=bsc.code")->leftJoin(['bsg'=>BaseGrade::tableName()],"bsc.grade_id=bsg.grade_id")->where(['mess.me_id'=>$me_id,'bsc.grade_id'=>$schExamMulti->me_grade_id]);//这儿省略了一些权限判断之类的...$subQuery->orderBy(['mess.mess_score'=>SORT_DESC]);//$list = $subQuery->all();$result = [];foreach($subQuery->each() as $key=>$list){//foreach ($list as $key=>$val){$result[$key]['准考证号'] = $list['student_id'];$result[$key]['姓名'] = $list['student_name'];$result[$key]['班级'] = $list['grade_name'].$list['class_name'];$result[$key]['学校'] = $list['school_name'];foreach ($subjects as $subid => $subname){$query = new Query();$query->select(['ess.ess_score'])->from(['ess'=>SchSingleExamStudentScore::tableName()])->leftJoin(['es'=>SchExamSingle::tableName()],"es.se_id=ess.se_id")//->leftJoin(['bc'=>BaseCourse::tableName()],"bc.course_id=es.se_course_id")->where(['ess.student_id'=>$list['student_id'],'ess.school_id'=>$list['school_id'],'es.se_course_id'=>$subid,'ess.me_id'=>$me_id]);$score = $query->one();$result[$key][$subname] = $score['ess_score'];}$result[$key]['总分'] = $list['mess_score'];$result[$key]['联考排名'] = $list['mess_order'];}return $result;
由上代码可见,特别是77-86行间,循环中再查询。整个接口下来最终耗时约14分钟的样子。
然后今天用了下字典:
<?php...# 获取年级字典 ['grade_id'=>'grade_name', ...]$gradeDict = (new MGrade())->getGradeNameDict();# 获取科目字典 ['course_id'=>'course_name', ...]$courseDict = (new MCourse())->getCourseDict();# 获取某次考试下的信息字典 ['se_id'=>['se_id','se_course_id','...']]$singExamDict = $this->getSingExamDict($me_id);foreach ($singExamDict as $item){$head[] = $courseDict[$singExamDict[$item['se_id']]['se_course_id']];}# 获取某次考试所有学生成绩信息/*** ['student_id1'=>* [* 'se_id1'=>[...],* 'se_id2'=>[...],* 'se_id3'=>[...],* ...* ]* ...* ]*/$studentSingExamScoreDict = $this->getStudentSingScoreDict($me_id);$subQuery = new Query();$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'])->from(['mess'=>SchMultiExamStudentScore::tableName()])->leftJoin(['bsc'=>BaseSchoolClass::tableName()],"mess.school_id=bsc.school_id AND mess.class_id=bsc.code")//->leftJoin(['bsg'=>BaseGrade::tableName()],"bsc.grade_id=bsg.grade_id")->where(['mess.me_id'=>$me_id,'bsc.grade_id'=>$schExamMulti->me_grade_id]);//这儿省略了一些权限判断筛选之类的..$subQuery->orderBy(['mess.mess_score'=>SORT_DESC]);//$list = $subQuery->all();$content = [];foreach($subQuery->each() as $key=>$list){//foreach ($list as $key=>$val){$content[$key][] = $list['student_id'];$content[$key][] = $list['student_name'];# 学校$content[$key][] = $list['school_name'];# 年级班级if(array_key_exists($list['grade_id'],$gradeDict)){$gradeName = $gradeDict[$list['grade_id']];}else{$gradeName = '-';}$content[$key][] = $gradeName.$list['class_name'];# 单科成绩foreach ($singExamDict as $exam){$se_id = $exam['se_id']; //单科id$student_id = $list['student_id']; //学生id//$course_name = $courseDict[$exam['se_course_id']]; //课程名称if(isset($studentSingExamScoreDict[$student_id][$se_id])){$content[$key][] = $studentSingExamScoreDict[$student_id][$se_id]['ess_score'];}else{$content[$key][] = '-';}}$content[$key][] = $list['mess_score'];$content[$key][] = $list['mess_order'];}$result['head'] = $head;$result['content'] = $content;return $result;
最终下来整个接口只用到4-5分钟的样子,比之前快了3倍,还是很厉害的。
当然这个速度肯定还是慢了,还需要优化,我再想看看有没有其它好方法再说。
然后贴一下其中一个字典的代码:
<?php/*** 获取学生某次考试的单科考试成绩信息* @param $me_id* @return array*/private function getStudentSingScoreDict($me_id){$dict = [];$query = new Query();$query->select(['student_id','se_id','ess_score',//总分'ess_class_order',//班级排名'ess_status',//状态'ess_grade_order',//年级排名'ess_order',//所有排名'ess_province_order',//省排名'ess_city_order',//市排名'ess_district_order',//地区排名]);$query->from(SchSingleExamStudentScore::tableName());$query->where(['me_id'=>$me_id]);$query->orderBy(['se_id'=>SORT_ASC]);$res = $query->all();foreach ($res as $item){$key = $item['student_id'];$dict[$key][$item['se_id']] = $item;}return $dict;}
