我使用的是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上是类似的。
我不确定为什么要添加这些额外的'或'检查为查询添加了太多时间。任何想法如何进行更高效的查询?
答案 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