Laravel连表查询,一个rid对应多条数据,但只需要一条数据,如何优化?

有A,B,C三张表,A、B表每一个rid对应一条数据,C表一个rid对应多条数据,三张表通过rid相关联,现在需求是,根据rid取出A、B表中数据,C表中只需要取出该rid对应多条数据的第一条。

想到的方法是,AB表join,取出符合要求的rid,然后对遍历每个rid,再去C表中取数据,并拿到第一条,大概是这个样子

// Laravel
$result = DB::table('A')
            ->join('B', DB::raw('A.rid'), '=', DB::raw('B.rid')
            ->where('condition', '=', 'somecondition')
            ->get(array(DB::raw(A.rid)));
        
$rows = array();

foreach ($result as $r) {
    $c = DB::table('C')->where('rid', '=', $r['rid'])->first();
    $rows[] = array(
        'rid' => $c['rid'],
        'key' => $c['value']
    );
}

return $rows;

现在的做法虽然能实现功能,但耗时太长了,请教下有什么优化方案吗?本来想读取C表时用whereIn,但取出来还要在筛选多余数据,不知道这种方法是不是比较好的实践方法?

阅读 6.5k
4 个回答

原生sql就是

select A.id,C.id from A
join C on A.rid = C.rid AND NOT EXISTS(select 1 FROM C c2 where c2.rid=C.rid and c2.id < C.id)

这时候的C就是id最小的一条
not exists解释:不存在一条比当前这条id还要小的,那这条就是最小那条了。
laravel写法,试试DB::raw()?

  • 首先wherein后去筛选数据,肯定比你在循环查c表快吧。。
  • 然后join('B', 'A.rid', '=', 'B.rid')不需要DB::raw

夜影的回答是个很棒的方案!

除了以上两个回答,还想到一个方案

$result = DB::table('A')
    ->join('B', DB::raw('A.rid'), '=', DB::raw('B.rid')
    ->where('condition', '=', 'somecondition')
    ->get();
    
$rids = [];
foreach($result as $key => $value) {
    $rids[] = $value['rid'];
}

$C = DB::table('C')
    ->select(DB::raw('min(id) as id, rid'))
    ->whereIn('rid', $rids)
    ->groupBy('rid')
    ->get(['rid', 'key1', 'key2']);

foreach($C as $key => $value) {
    选出$C中rid 等于 $value['rid'] 的数据
    // todo
}

大致想法就是先按照需要的rid在C表中使用whereIn取出所有数据,然后根据rid group by,再取出id最小的数据。但这种做法的弊端在于,取出数据后,还要和AB连表后的数据做一一映射。

非常感谢上面两位的回答!

同样的需求

    /**
     * 所有日志
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function hasLogs ()
    {
        return $this->hasMany( LeadLog::class, 'leads_id', 'id' )
            ->orderBy('track_at', 'desc');
    }

    /**
     * 最新一条日志
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function lastLog ()
    {
        return $this->hasOne(LeadLog::class, 'leads_id', 'id')
            ->select('id', 'leads_id','content', 'track_at')
            ->orderByDesc('id')
            ->groupBy('leads_id','id', 'track_at', 'content');
    }
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题