Laravel orWhere() / MySQL atau query memakan waktu lama

Saya menggunakan Laravel 4.2 dan aplikasi saya digunakan untuk melacak inventaris di beberapa lokasi.

Basis data diatur dengan tabel inventory_items, tabel inventory_locations, dan tabel pivot di antara keduanya inventory_items_inventory_location, yang berisi nilai kuantitas sambil merujuk pada item inventaris dan lokasi milik catatan tersebut.

Permintaan saya adalah menemukan item inventaris yang memiliki nilai kuantitas lokasi lebih dari atau sama dengan 0. Di Laravel saya menggunakan subquery dan orWhere seperti:

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

Yang memberikan SQL berikut:

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

Masalahnya adalah dengan pernyataan or (ditandai dalam kode dengan #slow) waktu kueri hingga 1 detik secara langsung dengan Sekuel Pro, lebih dari 5 detik melalui aplikasi Laravel saya (atau melalui artisan tinker). Tanpa pemeriksaan 'atau' ini (yaitu hanya memeriksa satu jenis kuantitas misalnya 'dipesan') kuerinya adalah ‹100 md di Sekuel Pro dan serupa di aplikasi/tinker.

Saya tidak yakin mengapa menambahkan tanda 'atau' tambahan ini menambah banyak waktu pada kueri. Adakah ide bagaimana membuat kueri yang lebih berkinerja?


person Mr Office    schedule 30.12.2016    source sumber
comment
sudahkah Anda menambahkan indeks pada bidang reserved, available, inbound, total tabel?   -  person num8er    schedule 30.12.2016
comment
Secara umum atau kondisi membuat kemungkinan eksekusi query. Mesin basis data juga tidak pernah membuat jalur statis, saat Anda menggunakan kondisi dinamis seperti ini. Jadi mungkin memerlukan waktu lebih lama dibandingkan jalur yang telah disiapkan   -  person Shankar Thiyagaraajan    schedule 30.12.2016
comment
@num8er ya, saya memiliki masing-masing indeks dan telah mencoba beberapa indeks juga (tidak yakin terminologi yang tepat untuk itu)   -  person Mr Office    schedule 30.12.2016
comment
@ShankarThiyagaraajan tidak 100% yakin dengan maksud Anda - dapatkah Anda memberikan contoh sederhana?   -  person Mr Office    schedule 30.12.2016
comment
Berapa banyak baris di tabel Anda? Selain itu, apakah saya benar berasumsi bahwa Anda dapat memiliki nilai negatif untuk available, inbound, dan total Anda?   -  person Rwd    schedule 30.12.2016
comment
@MrOffice jika Anda sudah menambahkan indeks di setiap bidang, jadi saya rasa Anda harus mengubah logika perbandingan dari >= ke > karena saya tidak melihat logika whereHas('inventoryLocations'). Maksud saya, Anda perlu memeriksa keberadaan inventoryLocations jika data lebih dari nol. karena >= mungkin mengembalikan semua data.   -  person num8er    schedule 30.12.2016
comment
@RossWilson Hanya 5 ribu baris. Nilai negatif diperbolehkan, namun saat ini tidak diperbolehkan.   -  person Mr Office    schedule 30.12.2016
comment
@num8er Terima kasih - perubahan perbandingan tidak mempengaruhi apa pun - Saya sudah mencoba dengan =, >, < dll.   -  person Mr Office    schedule 30.12.2016
comment
Terima kasih teman-teman, @jedzrej.kurylo berhasil.   -  person Mr Office    schedule 30.12.2016
comment
@Mr Office, Secara umum kelas akan membuat filter level Langkah-demi-Langkah. Tapi orWhere sedikit berbeda. Proses Filter akan berubah total dengan atau tanpa filter sebelumnya (Di mana sebelumnya). Jadi setiap kali Anda menggunakan orWhere, alirannya akan berubah secara dinamis. Jadi mungkin diperlukan penundaan waktu yang nyata.   -  person Shankar Thiyagaraajan    schedule 31.12.2016
comment
@Tuan Kantor, Ex. Pertimbangkan, Daftar Siswa. Total 100 catatan sebagai Nama|Nama Depan|Nama Belakang|Email. Query : pilih * dari siswa dengan nama belakang seperti '%john%' atau email seperti '%penjualan%'; Di sini, ini menghasilkan 3 kemungkinan Probabilitas, 1. Nama Belakang seperti '%john%', 2. email seperti '%sales%', 3. Nama Belakang seperti '%john%' atau email seperti '%sales%' Ketiganya akan diaktifkan secara Dinamis !   -  person Shankar Thiyagaraajan    schedule 31.12.2016


Jawaban (1)


Lihat kueri yang dihasilkan dan kondisi WHERE-nya. Anda pasti melewatkan beberapa tanda kurung di sana, karena menurut saya yang Anda butuhkan adalah

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

alih-alih

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

Ini menghasilkan pemindaian tabel penuh yang sangat lambat untuk tabel dengan jumlah baris yang banyak.

Untuk memperbaikinya, gantilah

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

dengan

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

Lihat perintah JELASKAN MySQL yang memungkinkan Anda menganalisis bagaimana kueri akan dieksekusi dan berapa banyak baris yang akan dikueri - http://dev.mysql.com/doc/refman/5.7/en/explain.html

person jedrzej.kurylo    schedule 30.12.2016
comment
Sempurna - masuk akal untuk melakukan subkueri sekarang saya tahu lebih banyak tentang cara kerjanya. Akan mempertimbangkan penjelasan di masa mendatang untuk men-debug masalah semacam ini. Terima kasih - person Mr Office; 30.12.2016