【问题标题】:Optimization of 3 Eloquent requests (1.5 seconds of request duration...)优化 3 个 Eloquent 请求(请求持续时间 1.5 秒...)
【发布时间】:2021-12-09 18:44:42
【问题描述】:

我想优化这 3 个查询,使我能够根据日期条件(当前周、上周、自开始以来)显示在我们网站上发布最多照片的用户。

用调试栏测得查询时间为1.5秒,真的很长! 你知道怎么优化吗?

public function show()
{
    $currentWeek = User::whereHas('pictures')
        ->whereHas('pictures', fn ($q) => $q->whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()]))
        ->withCount(['pictures' => fn ($q) => $q->whereBetween('created_at', [Carbon::now()->startOfWeek(), Carbon::now()->endOfWeek()])])
        ->orderBy('pictures_count', 'DESC')
        ->limit(10)
        ->get();

    $lastWeek = User::whereHas('pictures')
        ->whereHas('pictures', fn ($q) => $q->whereBetween('created_at', [Carbon::now()->startOfWeek()->subWeek(), Carbon::now()->endOfWeek()->subWeek()]))
        ->withCount(['pictures' => fn ($q) => $q->whereBetween('created_at', [Carbon::now()->startOfWeek()->subWeek(), Carbon::now()->endOfWeek()->subWeek()])])
        ->orderBy('pictures_count', 'DESC')
        ->limit(10)
        ->get();

    $overall = User::whereHas('pictures')
        ->whereHas('pictures')
        ->withCount('pictures')
        ->orderBy('pictures_count', 'DESC')
        ->limit(10)
        ->get();

    return view('users.leaderboard', [
        'currentWeek' => $currentWeek,
        'lastWeek' => $lastWeek,
        'overall' => $overall,
    ]);
}

【问题讨论】:

  • 您是否尝试创建数据库列“created_at”的index
  • 不,从来没有,你能解释更多吗? :)
  • 是的。索引与在任何书籍的开头列出索引的方式完全相同,以在特定页面上按名称查找项目。数据库使用索引来做同样的事情。例如,您有一个包含 1,00,000 个数据的表,并且您查询以搜索名称“xyz”。因此,在正常情况下,数据库会从头到尾查找“xyz”,并将遍历所有 1,00,000 条记录以找到“xyz”。但是,如果您在“名称”列上创建索引,那么数据库只会查看名称以“x”开头的位置,并且不需要查看所有 1,00,000 条记录,因此查询结果会快得多
  • 是的,更多数据需要更多时间才能访问
  • 好的,感谢您的宝贵帮助和您的宝贵时间;)

标签: php mysql laravel laravel-query-builder


【解决方案1】:

首先,你已经在图片关系上调用了两次whereHas,所以你可以摆脱不合格的调用。

$currentWeek = User::whereHas('pictures', fn ($q) => $q->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()]))
    ->withCount(['pictures' => fn ($q) => $q->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])])
    ->orderBy('pictures_count', 'DESC')
    ->limit(10)
    ->get();

这减少了 SQL 查询:

select `users`.*, (
    select count(*) from `pictures` where `users`.`id` = `pictures`.`user_id` and `created_at` between ? and ? and `pictures`.`deleted_at` is null
) as `pictures_count`
from `users`
where exists (select * from `pictures` where `users`.`id` = `pictures`.`user_id` and `pictures`.`deleted_at` is null)
    and exists (select * from `pictures` where `users`.`id` = `pictures`.`user_id` and `created_at` between ? and ? and `pictures`.`deleted_at` is null)
    and `users`.`deleted_at` is null
    order by `pictures_count` desc
    limit 10

到这里:

select `users`.*, (
    select count(*) from `pictures` where `users`.`id` = `pictures`.`user_id` and `created_at` between ? and ? and `pictures`.`deleted_at` is null
) as `pictures_count`
from `users`
where exists (select * from `pictures` where `users`.`id` = `pictures`.`user_id` and `created_at` between ? and ? and `pictures`.`deleted_at` is null)
    -- no second where exists clause
    and `users`.`deleted_at` is null
    order by `pictures_count` desc
    limit 10

现在,where 子句中只有一个条件。它选择在指定日期范围内拥有图片的用户。看起来更好,对吧?


但是,您已经在使用带有闭包的 withCount,因此您只计算日期范围内的图片。如果条件不匹配会怎样?它返回零。由于无论如何您都是按计数进行反向排序,因此对 whereHas 的另一个调用也可以进行。

$currentWeek = User::withCount(['pictures' => fn ($q) => $q->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()])])
    ->orderBy('pictures_count', 'DESC')
    ->limit(10)
    ->get();

现在您的 SQL 如下所示:

select `users`.*, (
    select count(*) from `pictures` where `users`.`id` = `pictures`.`user_id` and `created_at` between ? and ? and `pictures`.`deleted_at` is null
) as `pictures_count`
    from `users`
    where `users`.`deleted_at` is null
    -- no where exists clauses at all any more
    order by `pictures_count` desc
    limit 10

而且它应该运行得更快。这确实会稍微改变您的数据;结果集合总是有 10 个项目,即使其中一些是零。如果您不想在排行榜中出现零,只需将它们从集合中过滤掉即可。

【讨论】:

  • 非常感谢您的回答!我会应用这一切。我将应用所有这些。谢谢!
猜你喜欢
  • 2016-03-18
  • 2015-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 2021-12-02
  • 2015-08-27
  • 2015-08-02
相关资源
最近更新 更多