2016-12-30 4 views
1

Я использую Laravel 4.2, и мое приложение используется для отслеживания инвентаря в нескольких местах.Laravel orWhere()/MySQL или запрос занимает много времени

База данных создана с inventory_items стола, inventory_locations таблицы и сводной таблицы между ними inventory_items_inventory_location, который содержит значения количественных в то время как ссылки как элемент инвентаризации и местоположение рекорд принадлежит.

Мой запрос, чтобы найти элементы инвентаризации, которые имеют какое-либо значение местонахождения количество больше или равное 0. В Laravel я использую подзапрос и orWhere как так:

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) время запроса до 1s непосредственно с Sequel Pro, более 5с через мое приложение Laravel (или через ремесленник повозиться). Без этих «или» проверок (т. Е. Просто проверяя один тип количества, например «зарезервировано»), запрос равен < 100 мс на Sequel Pro и аналогичен в приложении/tinker.

Я не уверен, почему добавление этих дополнительных «или» проверок добавляет столько времени на запрос. Любые идеи, как сделать более результативный запрос?

+1

У вас есть указатели на поля 'зарезервированные',' доступные', 'входящие',' total' таблицы? – num8er

+0

В общем случае «или» условия делают возможными выполнение запроса. Кроме того, механизм базы данных никогда не делает статический путь, когда вы используете эти типы динамических условий. Так что может потребоваться больше времени, чем на подготовленный путь –

+0

@ num8er да, у меня есть индексы на каждом и попробовали несколько индексов (не уверен в правильной терминологии для этого) –

ответ

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(); 

ЗАКАНЧИВАТЬ EXPLAIN команду MySQL, которая позволяет проанализировать, как запрос будет выполнен и сколько строк будет запрошен - http://dev.mysql.com/doc/refman/5.7/en/explain.html

+0

Perfect - имеет смысл подзапроса, теперь я знаю немного больше о том, как это работает. Позаботимся об этом в будущем, чтобы отладить эту проблему. благодаря –

Смежные вопросы