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
: Nurab是Tags
: 酒精饮料
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 Eloquent whereHas() 查询 - 非常慢