【问题标题】:Optimize Laravel Eloquent whereHas() query - very slow优化 Laravel Eloquent whereHas() 查询 - 非常慢
【发布时间】:2018-02-27 02:34:30
【问题描述】:

我正在使用 Laravel 5.4

我有 3 个模型:Order、OrderLine 和 Product。

Order hasMany() OrderLines OrderLine hasOne() Product 通过 OrderLine 模型中的 product_id (我已经正确索引了这个,至少我认为!)

我的要求是检索产品属于某个品牌名称的所有订单和订单行。

这是我雄辩的问题。我知道查询有效,但是当放在大型数据集(大约 10,000 个订单,12,000 个订单行/产品)上时,它似乎无限运行

$orders = Order::whereBetween('order_date', [$this->start_date,$this->end_date])
    ->whereHas('lines', function ($q1){
        $q1->whereHas('product', function ($q2){
            $q2->where('brand', 'Brands>SanDisk');
        });
    })->with('lines')->with('lines.product')->get()->toArray();

通过 toSql() 方法调试时,这会产生以下 SQL。

select
   *
from `orders`
where
   `order_date` between ? and ? 
and
  exists (select * from `order_lines` where `orders`.`id` =`order_lines`.`order_id` 
and
  exists (select * from `products` where `order_lines`.`product_id` = `products`.`id` and `brand` = ?))

我创建表的 3 次迁移如下(为简单起见,我删除了除键之外的任何内容):

Schema::create('orders', function (Blueprint $table) {
    $table->increments('id');
});

Schema::create('order_lines', function (Blueprint $table) {
    $table->increments('id');
    $table->integer('product_id');
    $table->integer('order_id');
});

Schema::create('products', function (Blueprint $table) {
    $table->increments('id');
});

然后我添加了以下索引:

Schema::table('order_lines', function (Blueprint $table) {
    $table->integer('product_id')->unsigned()->change();
    $table->foreign('product_id')->references('id')->on('products');
});

EXPLAIN 语法结果如下:

1   PRIMARY orders  ALL                 91886   Using where 
2   DEPENDENT SUBQUERY  order_lines ALL                 93166   Using where 
3   DEPENDENT SUBQUERY  products    eq_ref  PRIMARY PRIMARY 4   mymemory_main.order_lines.product_id    1   Using where 

【问题讨论】:

  • 优化问题应包括表订单、order_lines 和产品之外的 CREATE TABLE 结构。以及 EXPLAIN [query] 输出...
  • 您可能希望使用EXPLAIN 语法检查mysql 控制台中的子查询,但我至少推荐order_lines.order_idorder_lines.product_id 上的索引
  • 能否请您说明您是如何索引这些表格的?
  • 生成的 SQL 查询生成非常非常糟糕。生成的 SQL 将采用两个相关的子查询。相关子查询是性能最差的一种。
  • @RaymondNijland 我在问题正文中添加了 EXPLAIN 的结果

标签: php mysql sql laravel eloquent


【解决方案1】:

试试这个:

$orders = Order::query()
    ->whereBetween('order_date', [$this->start_date, $this->end_date])
    ->hasByNonDependentSubquery('lines.product', null, function ($q) {
        $q->where('brand', 'Brands>SanDisk');
    })
    ->with('lines.product')
    ->get()
    ->toArray();

就是这样。祝你生活愉快!

【讨论】:

    猜你喜欢
    • 2018-05-27
    • 2021-07-22
    • 2021-05-25
    • 2018-01-30
    • 2017-10-07
    • 1970-01-01
    • 2017-11-17
    • 2021-05-28
    • 2020-09-24
    相关资源
    最近更新 更多