Laravel 搜索查询优化

Posted

技术标签:

【中文标题】Laravel 搜索查询优化【英文标题】:Laravel search query optimization 【发布时间】:2021-08-23 19:48:58 【问题描述】:

希望你做得好!

我的数据库中有超过 4500 个businesses。每个business hasMany tags。所以我在导航栏中有我的主要搜索输入。当用户通过该搜索输入提交任何字符串时,我想在其 name 字段中的任何 tags 或/和 中显示包含此 string 的所有企业。

示例:当string = ab 时。它必须显示前 20 个businesses,在其任何tag 名称或/和 name 字段中包含string ab

结果:

    Name: NurabTags: 酒精饮料 Name: Boirs Tags: 手机、手机配件、Tablet Name: Babilon Tags: Cable 电视、移动网络运营商 ......

生成并执行了这个 Eloquesnt/DB 查询

$businesses = Business::
            ->select('businesses.*')
            ->leftJoin('business_tag', 'businesses.id', '=', 'business_tag.business_id')
            ->leftJoin('tags', 'business_tag.tag_id', '=', 'tags.id')
            ->orWhere("tags.$this->lang_name", 'LIKE', "%$str%")
            ->orWhere('businesses.name', 'LIKE', "%$str%")
            
            ->where('businesses.status', true)
            ->groupBy('businesses.id')
            ->with(['tags'])
            ->withCount(['reviews as rating' => function($query) 
                $query->select(DB::raw('round(avg(rating), 1)')); 
            ])
            ->withCount('reviews')
            ->with(['reviews' => function($query)
                $query->latest();
            ])
            ->with(['images' => function ($query)
                
                    $query->where('avatar', true);
                ])
            ->paginate(20);

问题在于执行需要 21.05s。这是我的调试器显示的。以下是花费大部分时间的两个查询:

6.24s

select count(*) as aggregate from `businesses` 
left join `business_tag` on `businesses`.`id` = `business_tag`.`business_id` 
left join `tags` on `business_tag`.`tag_id` = `tags`.`id` 
where (`tags`.`en_name` LIKE '%ab%' or `businesses`.`name` LIKE '%ab%') 
and `businesses`.`status` = 1 group by `businesses`.`id`

14.78s

select `businesses`.*, 
     (select round(avg(rating), 1) from `reviews` 
      where `businesses`.`id` = `reviews`.`business_id` and `status` = 1) as `rating`,

     (select count(*) from `reviews` where `businesses`.`id` = `reviews`.`business_id` 
      and `status` = 1) as `reviews_count` from `businesses` 
left join `business_tag` on `businesses`.`id` = `business_tag`.`business_id` 
left join `tags` on `business_tag`.`tag_id` = `tags`.`id` 
where (`tags`.`en_name` LIKE '%ab%' or `businesses`.`name` LIKE '%ab%') 
and `businesses`.`status` = 1 group by `businesses`.`id` limit 20 offset 0

但是当我像这样评论那个标签部分时

$businesses = Business::
            ->select('businesses.*')
            // ->leftJoin('business_tag', 'businesses.id', '=', 'business_tag.business_id')
            // ->leftJoin('tags', 'business_tag.tag_id', '=', 'tags.id')
            // ->orWhere("tags.$this->lang_name", 'LIKE', "%$str%")
            ->orWhere('businesses.name', 'LIKE', "%$str%")

我需要 40.47ms 来执行。 如果我像这样评论名称部分

$businesses = Business::
            ->select('businesses.*')
            ->leftJoin('business_tag', 'businesses.id', '=', 'business_tag.business_id')
            ->leftJoin('tags', 'business_tag.tag_id', '=', 'tags.id')
            ->orWhere("tags.$this->lang_name", 'LIKE', "%$str%")
            // ->orWhere('businesses.name', 'LIKE', "%$str%")

需要 90.84 毫秒

现在的问题是:有没有办法优化这个查询? 先感谢您!祝你有美好的一天!

【问题讨论】:

【参考方案1】:

试试下面这个。看看执行需要多少时间。Aslo 代替 lefjoin 你可以在Business Model 中为Tags 创建belongsToMany 关系

$businesses = Business::
        select('businesses.*')
        ->leftJoin('business_tag', 'businesses.id', '=', 'business_tag.business_id')
        ->leftJoin('tags', 'business_tag.tag_id', '=', 'tags.id')
        ->where(function ($q)use($str)
            $q->where('businesses.status', true);
            $q->orWhere("tags.$this->lang_name", 'LIKE', "%$str%");
            $q->orWhere('businesses.name', 'LIKE', "%$str%");
        )
        ->groupBy('businesses.id')
        ->withCount(['reviews as rating' => function($query)
            $query->select(DB::raw('round(avg(rating), 1)'));
        ])
        ->withCount('reviews')
        ->with(['reviews' => function($query)
            $query->latest();
        ])
        ->with(['images' => function ($query)
        
            $query->where('avatar', true);
        ])
        ->paginate(20);

【讨论】:

花了 21.98 秒,是的,我有 belongsToMany 关系,但似乎加入执行所需的时间更少 okay.since you have leftjoin tags so remove ->with(['tags']) okay.np.lets 看看其他人的回答是否可以帮助您改进查询

以上是关于Laravel 搜索查询优化的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 关系查询优化

Laravel - 使用大数据优化更新/插入查询的最佳方法是啥?

优化 Laravel Eloquent whereHas() 查询 - 非常慢

Laravel - 如何优化 MIN - MAX - orderBy 查询?

优化地理搜索查询

Mysql查询优化——范围搜索