在拉拉维尔表现不佳的地方

2022-08-30 16:13:22

我想将条件应用于关系。以下是我的工作:where

Replay::whereHas('players', function ($query) {
    $query->where('battletag_name', 'test');
})->limit(100);

它生成以下查询:

select * from `replays` 
where exists (
    select * from `players` 
    where `replays`.`id` = `players`.`replay_id` 
      and `battletag_name` = 'test') 
order by `id` asc 
limit 100;

在70秒内执行。如果我手动重写查询,如下所示:

select * from `replays` 
where id in (
    select replay_id from `players` 
    where `battletag_name` = 'test') 
order by `id` asc 
limit 100;

它在0.4秒内执行。如果速度如此之慢,为什么默认行为?有没有办法使用查询生成器生成正确的查询,或者我是否需要注入原始SQL?也许我完全做错了什么?where existswhere in

replays表有400万行,有4000万行,所有相关列都已编入索引,数据集不适合MySQL服务器内存。players

更新:发现正确的查询可以生成为:

Replay::whereIn('id', function ($query) {
    $query->select('replay_id')->from('players')->where('battletag_name', 'test');
})->limit(100);

仍然有一个问题,为什么表现如此糟糕,为什么它是默认行为exists


答案 1

laravel有时缓慢的原因是使用where存在的语法实现。has(whereHas)

例如:

// User hasMany Post
User::has('posts')->get();
// Sql: select * from `users` where exists (select * from `posts` where `users`.`id`=`posts`.`user_id`)

“exists”语法是指向外部表的循环,然后每次都查询内部表(subQuery)。

但是,当用户表具有大量数据时,由于上面的sql无法使用索引,因此会出现性能问题。select * from 'users' where exists...

它可以代替这里使用而不会损坏结构。where inwhere exists

// select * from `users` where exists (select * from `posts` where `users`.`id`=`posts`.`user_id`)
// =>
// select * from `users` where `id` in (select `posts`.`user_id` from `posts`)

这将大大提高性能!

我建议你试试这个包 hasin,在上面的例子中,你可以用 代替 .hasinhas

// User hasMany Post
User::hasin('posts')->get();
// Sql: select * from `users` where `id` in (select `posts`.`user_id` from `posts`)

与框架相比只是只在语法上的地方而不是在任何地方存在,但是在其他地方都是一样的,比如参数调用模式甚至是代码实现,并且可以安全地使用。hasinhas


答案 2

试试这个:

Replay::hasByNonDependentSubquery('players', function ($query) {
    $query->where('battletag_name', 'test');
})->limit(100);

就这样。快乐雄辩的生活!


推荐