【问题标题】:Poor query performance with whereHas()whereHas() 的查询性能不佳
【发布时间】:2020-09-08 22:35:26
【问题描述】:

我已经与这个问题作斗争了几个星期了。

我有一个 Laravel 7 应用程序,它在执行某个查询时似乎绝对会影响数据库。首先,我将概述问题,然后更深入地研究我试图隔离它的内容。

我有一个 Opportunity 模型,该模型上有一个 scopeLucrative() 范围,用于过滤机会,只显示用户定义的阈值之间的机会,如下所示:

public function scopeLucrative($query)
    {

        $user = auth()->user();
        $threshold = $user->preference('quality.threshold');
        $expiredThreshold = $user->preference('quality.expired_threshold');
        $hideOwnReports = $user->preference('quality.hide_own_price_changed_reports');

        /**
         * For an Opportunity to be an lucrative one, the value has to be over the user's threshold.
         */

        // Where probability is over the user's threshold
        return $query->where('probability', '>=', $threshold)

            // And where the number of false reports is less than the user's expired threshold
            ->whereHas('verifiedPriceReports', function ($report) {

                $report->where('correct_price', false)->distinct('user_id')->take(15);
            }, '<', $expiredThreshold)

            // And when the user has 'hide_own_price_changed_reports' on, hide ones they've marked as incorrect
            ->when($hideOwnReports, function ($query) use ($user) {

                return $query->whereDoesntHave('verifiedPriceReports', function ($report) use ($user) {

                    $report->where('user_id', $user->id)->where('correct_price', false);
                });
            });
    }

当像 Opportunity::with('history', 'verifiedPriceReports')-&gt;lucrative()-&gt;orderByDesc('updated_at')-&gt;paginate(10)) 这样调用时,根据 DigitalOcean 控制面板,数据库似乎正在获取大量行(并且需要 600 毫秒),尽管由于分页,查询只返回了预期的 10 行。

您可以想象,这不能很好地扩展。只有 5 个活动用户,数据库查询开始需要几秒钟才能返回。该查询生成器执行的查询是:

SELECT *
FROM `opportunities`
WHERE `probability` >= '-15'
  and (SELECT distinct count(*)
       FROM `opportunity_reports`
       WHERE `opportunities`.`id` = `opportunity_reports`.`opportunity_id`
         and `correct_price` = '0'
         and `updated_at` >= '2020-09-06 04:20:17') < 3
  and not exists(SELECT *
                 FROM `opportunity_reports`
                 WHERE `opportunities`.`id` = `opportunity_reports`.`opportunity_id`
                   and `user_id` = '1'
                   and `correct_price` = '0'
                   and `updated_at` >= '2020-09-06 04:20:17')
ORDER BY `probability` DESC
LIMIT 10 offset 0;

很快就将问题缩小到scopeLucrative,只需简单地调用模型,而lucrative 作用域没有像Opportunity::with('history', 'verifiedPriceReports')-&gt;orderByDesc('updated_at')-&gt;paginate(10)) 那样按预期执行。

对于如何解决这个问题,我一头雾水。有没有人经历过这样的事情?

【问题讨论】:

  • 不确定是否有帮助,但我曾经与whereHas() 有过类似的问题,我追踪了实际执行的查询,结果发现由于不兼容,它总是在查询整个表标识类型。 uuid 被自动转换为 0 并搞砸了一切。
  • 感谢您的建议,但运气不佳 - 我没有在任何表中使用 UUID。

标签: mysql sql laravel


【解决方案1】:

我通过将whereHas() 替换为whereRaw() 解决了这个问题

->whereRaw('opportunities.id not in (SELECT opportunity_id
                  FROM  opportunity_reports
                    WHERE correct_price = false
                  GROUP BY opportunity_id
                  HAVING COUNT(*) >= '.$expiredThreshold.'
              )')

【讨论】:

  • WhereRaw 基本上是 RAW SQL 查询。你能用你当前的吞吐量和延迟图更新你的问题吗?
猜你喜欢
  • 1970-01-01
  • 2013-09-22
  • 1970-01-01
  • 2021-06-09
  • 1970-01-01
  • 1970-01-01
  • 2016-06-21
  • 2014-09-10
  • 1970-01-01
相关资源
最近更新 更多