优化 Laravel Eloquent whereHas() 查询 - 非常慢
Posted
技术标签:
【中文标题】优化 Laravel Eloquent whereHas() 查询 - 非常慢【英文标题】:Optimize Laravel Eloquent whereHas() query - very slow 【发布时间】: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_id
和order_lines.product_id
上的索引
能否请您说明您是如何索引这些表格的?
生成的 SQL 查询生成非常非常糟糕。生成的 SQL 将采用两个相关的子查询。相关子查询是性能最差的一种。
@RaymondNijland 我在问题正文中添加了 EXPLAIN 的结果
【参考方案1】:
试试这个:
mpyw/eloquent-has-by-non-dependent-subquery: Convert has() and whereHas() constraints to non-dependent subqueries. mpyw/eloquent-has-by-join: Convert has() and whereHas() constraints to join() ones for single-result relations.$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();
就是这样。祝你生活愉快!
【讨论】:
以上是关于优化 Laravel Eloquent whereHas() 查询 - 非常慢的主要内容,如果未能解决你的问题,请参考以下文章
Eloquent:find() 和 where() 用法 laravel
Laravel Eloquent - 返回 Where Has + Where [重复]
如何处理带有斜线的 Laravel Eloquent “WHERE”查询? (Laravel 5.3)
如何使用 Laravel Eloquent 创建多个 Where 子句查询?