Laravel orWhere() / MySQL หรือการสืบค้นใช้เวลานาน

ฉันใช้ 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) เวลาในการสืบค้นสูงถึง 1 วินาทีโดยตรงกับ Sequel Pro มากกว่า 5 วินาทีผ่านแอป Laravel ของฉัน (หรือผ่านช่างซ่อม) หากไม่มีการตรวจสอบ 'หรือ' เหล่านี้ (เช่น เพียงตรวจสอบประเภทปริมาณหนึ่งประเภท เช่น 'สงวนไว้') ข้อความค้นหาจะอยู่ที่ ‹100ms บน Sequel Pro และคล้ายกันในแอป/คนจรจัด

ฉันไม่แน่ใจว่าเหตุใดการเพิ่มเช็ค 'หรือ' พิเศษเหล่านี้จึงเพิ่มเวลาให้กับการค้นหามาก มีแนวคิดใดบ้างที่จะสร้างแบบสอบถามที่มีประสิทธิภาพมากขึ้น


person Mr Office    schedule 30.12.2016    source แหล่งที่มา
comment
คุณได้เพิ่มดัชนีลงในฟิลด์ reserved, available, inbound, total ของตารางแล้วหรือยัง?   -  person num8er    schedule 30.12.2016
comment
โดยทั่วไปหรือเงื่อนไขทำให้เกิดความเป็นไปได้ในการดำเนินการค้นหา นอกจากนี้กลไกฐานข้อมูลจะไม่สร้างเส้นทางแบบคงที่เมื่อคุณใช้เงื่อนไขไดนามิกประเภทนี้ ดังนั้นจึงอาจใช้เวลานานกว่าเส้นทางที่เตรียมไว้   -  person Shankar Thiyagaraajan    schedule 30.12.2016
comment
@ num8er ใช่ ฉันมีดัชนีในแต่ละดัชนีและได้ลองใช้หลายดัชนีด้วย (ไม่แน่ใจว่าคำศัพท์ที่เหมาะสมสำหรับสิ่งนั้น)   -  person Mr Office    schedule 30.12.2016
comment
@ShankarThiyagaraajan ไม่แน่ใจ 100% ว่าคุณหมายถึงอะไร - คุณช่วยยกตัวอย่างง่ายๆ ได้ไหม?   -  person Mr Office    schedule 30.12.2016
comment
ตารางของคุณมีกี่แถว? นอกจากนี้ ฉันคิดถูกไหมที่สมมติว่าคุณสามารถมีค่าลบสำหรับ available, inbound และ total ของคุณได้   -  person Rwd    schedule 30.12.2016
comment
@MrOffice หากคุณได้เพิ่มดัชนีในทุกฟิลด์แล้ว ดังนั้นฉันสามารถเดาได้ว่าคุณจะเปลี่ยนตรรกะของการเปรียบเทียบจาก >= เป็น > เพราะฉันไม่เห็นตรรกะของ whereHas('inventoryLocations') ฉันหมายถึงคุณต้องตรวจสอบการมีอยู่ของ inventoryLocations หากข้อมูลมีค่ามากกว่าศูนย์ เพราะ >= อาจส่งคืนข้อมูลทั้งหมด   -  person num8er    schedule 30.12.2016
comment
@RossWilson เพียง 5,000 แถว อนุญาตให้ใช้ค่าลบได้ แต่ขณะนี้ยังไม่มี   -  person Mr Office    schedule 30.12.2016
comment
@ num8er ขอบคุณ - การเปลี่ยนแปลงการเปรียบเทียบไม่มีผลกระทบใด ๆ - ฉันได้ลองด้วย =, >, < เป็นต้น   -  person Mr Office    schedule 30.12.2016
comment
ขอบคุณพวกคุณ @ jedzrej.kurylo ตอกมันไว้บนหัว   -  person Mr Office    schedule 30.12.2016
comment
@Mr Office โดยทั่วไปแล้วคลาสจะสร้างตัวกรองระดับทีละขั้นตอน แต่หรือมีความแตกต่างกันเล็กน้อย กระบวนการกรองจะเปลี่ยนแปลงทั้งหมดโดยมีหรือไม่มีตัวกรองก่อนหน้า (ก่อนหน้า Where) ดังนั้นทุกครั้งที่คุณใช้ orWhere มันจะเปลี่ยนโฟลว์แบบไดนามิก ดังนั้นจึงอาจต้องใช้เวลาล่าช้าอย่างเห็นได้ชัด   -  person Shankar Thiyagaraajan    schedule 31.12.2016
comment
@สำนักงานนายอดีต พิจารณารายชื่อนักเรียน ทั้งหมด 100 รายการเป็น Name|FirstName|LastName|Email ข้อความค้นหา : เลือก * จากนักเรียนโดยที่นามสกุลเช่น '%john%' หรืออีเมลเช่น '%sales%'; ที่นี่ มันสร้างความน่าจะเป็นที่เป็นไปได้ 3 แบบ 1. นามสกุลเช่น '%john%', 2. อีเมลเช่น '%sales%', 3. นามสกุลเช่น '%john%' หรืออีเมลเช่น '%sales%' ทั้งสามนี้จะเริ่มทำงานแบบไดนามิก !   -  person Shankar Thiyagaraajan    schedule 31.12.2016


คำตอบ (1)


ดูข้อความค้นหาผลลัพธ์และเงื่อนไข 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

person jedrzej.kurylo    schedule 30.12.2016
comment
สมบูรณ์แบบ - สมเหตุสมผลแล้วที่จะค้นหาย่อย ตอนนี้ฉันรู้มากขึ้นเกี่ยวกับวิธีการทำงานแล้ว จะพิจารณาอธิบายในอนาคตเพื่อแก้ไขปัญหาประเภทนี้ ขอบคุณ - person Mr Office; 30.12.2016