You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel查询构建器查询耗时优化求助:20秒获取2500条数据

Laravel 查询性能优化方案

针对你的查询耗时问题,核心优化方向是解决orOn关联导致的结果集膨胀、添加索引减少数据库扫描范围,以及优化关联逻辑避免不必要的重复数据处理,具体方案如下:

1. 重构feedback表关联逻辑,消除orOn的性能损耗

原查询中orOn会让数据库对每个customer匹配所有符合三个ID条件的feedback记录,导致结果集急剧膨胀,后续groupBy又要额外消耗资源去重。可以用以下两种方式替代:

方案A:用IN条件替代orOn

DB::table('customer as c')
    ->leftJoin('feedback as fr', function($leftJoin) {
        $leftJoin->whereRaw('fr.id IN (c.active_feedback_req_id_1, c.active_feedback_req_id_2, c.active_feedback_req_id_3)');
    })
    // 后续关联、条件、排序保持不变

方案B:子查询预取feedback数据(更高效)

如果每个customer最多对应一条feedback记录,直接用子查询获取对应数据,避免join带来的结果集冗余:

DB::table('customer as c')
    ->select([
        'c.id as customer_id',
        'c.name as customer_name',
        'c.created_at as customer_creation_date',
        // 其他customer字段...
        // 子查询获取feedback聚合数据
        DB::raw('(
            SELECT CONCAT(service_taken, "|", service_provider, "|", reminders_sent) 
            FROM feedback 
            WHERE id IN (c.active_feedback_req_id_1, c.active_feedback_req_id_2, c.active_feedback_req_id_3)
            LIMIT 1
        ) AS feedback_info'),
        // 其他字段...
    ])
    ->leftJoin('feedback_request_status as frs', function($join) {
        $join->on('fr.id', '=', 'frs.id');
    })
    ->where('c.business_location_id', $request->location_id)
    ->where('c.is_active', 1)
    ->orderBy('c.created_at','DESC')
    ->get();

后续可以在PHP代码中拆分feedback_info字段,提取对应的值。

2. 添加关键索引,减少数据库扫描范围

索引是提升查询速度的核心,针对你的查询创建以下索引:

  • customer表:覆盖查询过滤和排序条件
    CREATE INDEX idx_customer_location_active_created ON customer(business_location_id, is_active, created_at);
    CREATE INDEX idx_customer_feedback_ids ON customer(active_feedback_req_id_1, active_feedback_req_id_2, active_feedback_req_id_3);
    
  • customer_service表:加速关联查询,避免全表扫描
    CREATE INDEX idx_customer_service_customer ON customer_service(customer_id);
    -- 如果需要频繁获取service_taken和service_provider,添加覆盖索引
    CREATE INDEX idx_customer_service_customer_fields ON customer_service(customer_id, service_taken, service_provider);
    
  • feedback表:加速状态关联
    CREATE INDEX idx_feedback_status ON feedback(feedback_request_status_id);
    

3. 优化customer_service关联,避免结果集重复

原查询leftJoin customer_service会导致一个customer对应多条服务记录时,结果集重复,groupBy被迫去重消耗资源。改为只取每个customer的最新服务记录:

DB::table('customer as c')
    ->leftJoin(DB::raw('(
        SELECT cs.* 
        FROM customer_service cs
        INNER JOIN (
            SELECT customer_id, MAX(created_at) as latest_time
            FROM customer_service
            GROUP BY customer_id
        ) cs_latest ON cs.customer_id = cs_latest.customer_id AND cs.created_at = cs_latest.latest_time
    ) as cs'), 'c.id', '=', 'cs.customer_id')
    // 其他关联和条件保持不变

4. 修正GROUP BY与SELECT的逻辑一致性

原查询GROUP BY c.id时,SELECT包含了非customer表的非聚合字段(如fr.service_taken),在MySQL严格模式下会报错,且数据库会随机选取值。如果一个customer对应多条关联记录,需明确聚合方式:

->select([
    // customer字段...
    DB::raw('MAX(fr.service_taken) as service_taken'),
    DB::raw('MAX(fr.service_provider) as service_provider'),
    DB::raw('MAX(cs.service_taken) as customer_service_taken'),
    // 其他非customer字段同理
])

5. 可选:分页查询降低单次负载

如果业务允许,不要一次性获取2500条数据,改用分页:

->orderBy('c.created_at','DESC')
->paginate(50); // 每页50条,耗时会大幅降低

内容的提问来源于stack exchange,提问作者HarshGaikwad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 11:43:15