在迴圈中做查詢,看了自己介面效能驚呆了

FeiYu-Lin發表於2020-01-20

下面是擷取的部分程式碼

優化前程式碼

    /**
    * 柱狀圖 面試趨勢及通過人數
    * @param TrendRequestAlias $request
    * @return array
    */
    public function trend(TrendRequestAlias $request)
    {
        $date = $request->input('date');
        try {
            $data = $this->getTrendData($date[0], $date[1]);
            $this->result->succeed($data);
        } catch (\Exception $exception) {
            $this->result->failed($exception->getMessage());
        }
        return $this->result->toArray();
    }

    /**
    * 招聘渠道及通過率柱狀圖
    * @param ChannelRequest $request
    * @return array
    */
    public function channel(ChannelRequest $request)
    {
        $type = $request->input('type');
        $date = $request->input('date');
        try{
            $data = $this->disposeByType($type,$date);
            $this->result->succeed($data);
        }catch (\Exception $exception){
            $this->result->failed($exception->getMessage());
        }

        return $this->result->toArray();
    }

    /**
     * 查詢表格資料
     * @param string $start_time
     * @param string $end_time
     * @return array
     */
    private function getTrendData(string $start_time, string $end_time)
    {
        $dates = $this->getEveryDayDate($start_time, $end_time);  // 日期集合
        $intCount = [];
        $passCount = [];
        foreach ($dates as $date) {

            $query = InterviewRecord::query();
            $query = $query->whereDate('interview_time', $date)
                ->whereIn('type', [
                    InterviewRecord::TYPE_FIRST,
                    InterviewRecord::TYPE_SECOND,
                    InterviewRecord::TYPE_THIRD,
                    InterviewRecord::TYPE_OFFER,
                ]);
            ## 面試人數
            $intCount[] = $query->count();
            ## 通過人數
            $passCount[] = $query->where('result', InterviewInvitation::ABANDON_Y)->count();
        }

        return [
            'date' => $dates,
            'interviewCount' => $intCount,
            'passCount' => $passCount,
        ];
    }

    /**
     * 生成日/周/月 資料
     * @param string $type
     * @param array $date
     * @return array
     */
    private function disposeByType(string $type, array $date)
    {
        $channels = config('business.option.channel.items'); // 渠道集合
        $channels_value = array_column($channels,'value');
        $data = [];
        $result = [];
        switch ($type){
            case 'week':
                $dates = $this->getEveryWeekDate($date[0], $date[1]);
                $result['date'] = array_column($dates,'number');
                break;
            case 'month':
                $dates = $this->getEveryMonthDate($date[0], $date[1]);
                $result['date'] = array_column($dates,'number');
                break;
            case 'day':
                $dates = $this->getEveryDayDate($date[0], $date[1]);
                $result['date'] = $dates;
        }
        // 迴圈渠道
        foreach ($channels_value as $value){
            $data[] = $type == 'day' ?  $this->getDayDataByDate($value,$dates) : 
            $this->getWeekOrMonthDataByDate($value,$dates);
        }
        $result['channel'] = array_column($channels,'label');
        $data_tmp = [];
        foreach ($channels  as $key => $channel){
            $data_tmp[] = [
                'name' => $channel['label'],
                'data' => $data[$key]
            ];
        }
        $result['data'] = $data_tmp;
        return $result;
    }

    /**
    * 按日/月取資料
    * @param $channel
    * @param $dates 
    * @return array
    */
    private function getDayOrMonthDataByDate($channel, $dates)
    {
        $tmp = [];
        foreach ($dates as $date) {
            $tmp[] = DashboardChannel::where('channel',$channel)->whereDate('date' ,$date)->sum('pass_count');
        }
        return $tmp;
    }

    /**
    * 按周取資料
    * @param \Illuminate\Database\Eloquent\Builder $query
    * @param array $weeks 
    * @return array 
    */
    private function getWeekDataByDate($channel, array $weeks)
    {
        $tmp = [];
        foreach ($weeks as $week) {
            $tmp[] = DashboardChannel::where('channel',$channel)
                ->whereDate('date','>=' ,$week['start'])
                ->whereDate('date','<' ,$week['end'])
                ->sum('pass_count');
        }
        return $tmp;
    }

其實寫的時候知道這個問題,想著之後優化
對接完之後,時間引數選2016-12-06 至 2020-01-23, 資料庫基本無資料的情況下,響應時間3.57s

在迴圈中做查詢,看了自己介面效能驚呆了

意識到問題嚴重性之後..

    /**
     * 查詢表格資料
     * @param string $start_time
     * @param string $end_time
     * @return array
     */
    private function getTrendData(string $start_time, string $end_time)
    {
        $dates = $this->getEveryDayDate($start_time, $end_time);  // 日期集合
        // 邀請人數
        $interviews = InterviewRecord::whereIn('type',[
            InterviewRecord::TYPE_FIRST,
            InterviewRecord::TYPE_SECOND,
            InterviewRecord::TYPE_THIRD,
            InterviewRecord::TYPE_OFFER,
        ])->select('type','interview_time')
            ->whereDate('interview_time','>=',$start_time)
            ->whereDate('interview_time','<=',$end_time)
            ->pluck('interview_time')->toArray();

        // 通過人數
        $pass = InterviewRecord::whereIn('type',[
            InterviewRecord::TYPE_FIRST,
            InterviewRecord::TYPE_SECOND,
            InterviewRecord::TYPE_THIRD,
            InterviewRecord::TYPE_OFFER,
        ])->select('type','interview_time','result')
            ->whereDate('interview_time','>=',$start_time)
            ->whereDate('interview_time','<=',$end_time)
            ->where('result', InterviewInvitation::ABANDON_Y) //結果為通過
            ->pluck('interview_time')->toArray();

        // 面試時間格式化
        foreach ($interviews as &$item){
            $item = date('Y-m-d',strtotime($item));
        }
        // 統計每日面試人數
        $interviews = array_count_values($interviews);
        // 面試時間格式化
        foreach ($pass as &$item){
            $item = date('Y-m-d',strtotime($item));
        }
        $pass = array_count_values($pass);

        foreach ($dates as $date){
            $intCount[] = $interviews[$date] ?? 0;
            $passCount[] = $pass[$date] ?? 0;
        }

        return [
            'date' => $dates,
            'interviewCount' => $intCount,
            'passCount' => $passCount,
        ];
    }

    /**
    * 生成日/周/月 資料
    * @param string $type
    * @param array $date 
    * @return array * @throws \Exception */
    private function disposeByType(string $type, array $date)
    {
        $channels = config('business.option.channel.items'); // 渠道集合
        $start_date = $date[0];
        $end_date = $date[1];
        $result = []; //返回值
        $data = DashboardChannel::select('channel','date','pass_count')
            ->whereDate('date','>=',$start_date)
            ->whereDate('date','<=',$end_date)
            ->get()
            ->toArray();

        switch ($type){
            case 'day':
                $dates = $this->getEveryDayDate($start_date, $end_date);
                $result['date'] = $dates;
                break;
            case 'week':
                $dates = $this->getEveryWeekDate($start_date, $end_date);
                $result['date'] = array_column($dates,'number');
                break;
            case 'month':
                $dates = $this->getEveryMonthDate($start_date, $end_date);
                $result['date'] = array_column($dates,'number');
                break;
        }

        $data = $this->disposeByDate($data,$dates,$channels,$type);

        $result['channel'] = array_column($channels,'label');
        $result['data'] = $data;

        return $result;
    }

/**
 * 根據日期型別組裝資料(日/月/周/無型別(招聘通過率))
 * @param $data
 * @param array $dates 
 * @param array $channels
 * @param null $type
 * @return array
 * @throws \Exception
 */
 private function disposeByDate($data,array $dates,array $channels,$type = null)
 {
    if ($type && $type != 'day'){
        $numbers = array_column($dates,'number');
        $dates = array_combine($numbers,$dates);
    }
    // 拼裝初始資料框架
    $tmpStruct = [];
    foreach ($channels as $channel){
        $tmpStruct[$channel['value']] = [
            'name' => $channel['label'],
            'data' => []
        ];
        foreach ($dates as $date){
            $key = $date['number'] ?? $date;
            $tmpStruct[$channel['value']]['data'][$key] = 0;
        }
    }
    // 資料組裝
    foreach ($data as $item){
        if (!$type || $type == 'day'){
            // 日
            $number = $item['date'];
        }elseif ($type == 'week'){
            // 周
            $number = date('Y',strtotime($item['date'])).'年第'.date('W',strtotime($item['date'])).'周';
        }elseif ($type == 'month'){
            // 月
            $number = date('Y-m',strtotime($item['date']));
        }else{
            throw new \Exception('type不合法');
        }

        if ($type)
            $tmpStruct[$item['channel']]['data'][$number] += $item['pass_count'];
        else
            $tmpStruct[$item['channel']]['data'][$number] = $item['pass_rate'];  // 通過率(招聘通過率介面)
   }
        // 去除日期key
    foreach ($tmpStruct as &$item){
        $item['data'] = array_values($item['data']);
    }

    return array_values($tmpStruct);
}

修改完之後測了一下效果

在迴圈中做查詢,看了自己介面效能驚呆了

寫在最後

昂,菜鳥第一次發部落格,技術很渣渣,程式碼寫得也比較亂,請大家諒解...
雖然是個菜鳥,但是還是希望自己能寫出高質量,高效能,能獲得大家認同的程式碼

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章