Laravel orWhere()/ MySQL或查询需要很长时间

时间:2016-12-30 13:09:39

标签: php mysql laravel laravel-4 eloquent

我使用的是Laravel 4.2,我的应用程序用于跟踪多个位置的广告资源。

数据库设置有inventory_items表,inventory_locations表和它们之间的数据透视表inventory_items_inventory_location,其中包含数量值,同时引用了库存项目和记录位置属于。

我的查询是查找任何位置数量值大于或等于0的库存商品。在Laravel中我使用子查询和/或者这样:

InventoryItem::whereHas('inventoryLocations', function($q) {
  $q->where('reserved', '>=', 0)
     ->orWhere('available', '>=', 0) # slow
     ->orWhere('inbound', '>=', 0) # slow
     ->orWhere('total', '>=', 0); # slow
})->toSql();

这给出了以下SQL:

select * from `inventory_items`
where `inventory_items`.`deleted_at` is null
and (
  select count(*) from `inventory_locations`
  inner join `inventory_item_inventory_location`
  on `inventory_locations`.`id` = `inventory_item_inventory_location`.`inventory_location_id` 
  where `inventory_item_inventory_location`.`inventory_item_id` = `inventory_items`.`id`
  and `reserved` >= ?
  or `available` >= ? # slow
  or `inbound` >= ? # slow
  or `total` >= ? # slow
) >= 1

问题在于使用or语句(在#slow代码中标记),Sequel Pro的查询时间最多为1秒,通过我的Laravel应用程序(或通过工匠)超过5秒鼓捣)。没有这些'或'检查(即只检查一种数量类型,例如'保留')查询在Sequel Pro上是< 100ms,在app / tinker上是类似的。

我不确定为什么要添加这些额外的'或'检查为查询添加了太多时间。任何想法如何进行更高效的查询?

1 个答案:

答案 0 :(得分:3)

查看生成的查询及其 WHERE 条件。你肯定会错过一些括号,因为我猜你需要的是

where `inventory_item_inventory_location`.`inventory_item_id` = `inventory_items`.`id`
and (
   `reserved` >= ?
   or `available` >= ? #
   or `inbound` >= ?
   or `total` >= ?
)

而不是

where `inventory_item_inventory_location`.`inventory_item_id` = `inventory_items`.`id`
and `reserved` >= ?
or `available` >= ? # slow
or `inbound` >= ? # slow
or `total` >= ?

导致全表扫描,对于行数较多的表来说非常慢。

为了解决这个问题,请更换

InventoryItem::whereHas('inventoryLocations', function($q) {
  $q->where('reserved', '>=', 0)
   ->orWhere('available', '>=', 0) # slow
   ->orWhere('inbound', '>=', 0) # slow
   ->orWhere('total', '>=', 0); # slow
})->toSql();

InventoryItem::whereHas('inventoryLocations', function($q) {
  $q->where(function($subquery) {
    $subquery->where('reserved', '>=', 0)
     ->orWhere('available', '>=', 0)
     ->orWhere('inbound', '>=', 0)
     ->orWhere('total', '>=', 0);
  });
})->toSql();

查看MySQL的 EXPLAIN 命令,该命令可让您分析查询的执行方式以及查询的行数 - http://dev.mysql.com/doc/refman/5.7/en/explain.html