Хм, може да се опитам да напиша вашата заявка по следните редове:
SELECT Sale_Item.deleted, Sale_Item.deleted_by,
Sale_Item.sale_time, Sale_Item.sale_date,
Sale_Item.comment,
Sale_Item.payment_type,
Sale_Item.customer_id,
Sale_Item.employee_id,
Sale_Item.category,
Sale_Item.sale_id, Sale_Item.item_id, NULL as item_kit_id, Sale_Item.line,
Sale_Item.supplier_id,
Sale_Item.serialnumber, Sale_Item.description,
Sale_Item.quantity_purchased, Sale_Item.item_cost_price, Sale_Item.item_unit_price,
Sale_Item.discount_percent,
Sale_Item.lineSubtotal,
Sale_Item.lineSubtotal * COALESCE(Tax.non_cumulative, 0) + (Sale_Item.lineSubtotal * COALESCE(Tax.non_cumulative, 0) + Sale_Item.non_cumulative) * COALESCE(Tax.cumulative, 0) AS lineTax,
Sale_Item.lineSubtotal + (Sale_Item.lineSubtotal * COALESCE(Tax.non_cumulative, 0) + (Sale_Item.lineSubtotal * COALESCE(Tax.non_cumulative, 0) + Sale_Item.non_cumulative) * COALESCE(Tax.cumulative, 0)) AS lineTotal,
Sale_Item.lineSubtotal - (Sale_Item.item_cost_price * Sale_Item.quantity_purchased) AS profit
FROM (SELECT Sale.deleted, Sale.deleted_by,
Sale.sale_time, DATE(Sale.sale_time) AS sale_date,
Sale.comment,
Sale.payment_type,
Sale.customer_id,
Sale.employee_id,
Item.category,
Sale_Item.sale_id, Sale_Item.item_id, NULL as item_kit_id, Sale_Item.line,
Sale_Item.supplier_id,
Sale_Item.serialnumber, Sale_Item.description,
Sale_Item.quantity_purchased, Sale_Item.item_cost_price, Sale_Item.item_unit_price,
Sale_Item.discount_percent,
(Sale_Item.item_unit_price * Sale_Item.quantity_purchased) - (Sale_Item.item_unit_price * Sale_Item.quantity_purchased * Sale_Item.discount_percent / 100) as lineSubtotal
FROM phppos_sales_items Sale_Item
JOIN phppos_sales Sale
ON Sale.sale_id = Sale_Item.sale_id
AND Sale.sale_time >= TIMESTAMP('2014-04-01')
AND Sale.sale_time < TIMESTAMPADD(MONTH, 1, '2014-04-01')
AND Sale.location_id = 1
AND Sale.store_account_payment = 0) Sale_Item
LEFT JOIN (SELECT Tax.sale_id, Tax.item_id, Tax.line,
SUM(CASE WHEN Tax.cumulative = 1 THEN Tax.percent ELSE 0 END) as cumulative,
SUM(CASE WHEN Tax.cumulative <> 1 THEN Tax.percent ELSE 0 END) as non_cumulative
FROM phppos_sales_item_taxes Tax
JOIN phppos_sales Sale
ON Sale.sale_id = Tax.sale_id
AND Sale.sale_time >= TIMESTAMP('2014-04-01')
AND Sale.sale_time < TIMESTAMPADD(MONTH, 1, '2014-04-01')
AND Sale.location_id = 1
AND Sale.store_account_payment = 0
GROUP BY Tax.sale_id, Tax.item_id, Tax.line) Tax
ON Tax.sale_id = Sale_Item.sale_id
AND Tax.item_id = Sale_Item.sale_id
AND Tax.line =Sale_Item.line
Преместени няколко колони за организационни цели. Това не би трябвало да има голям ефект върху времето за обработка.
Премахнах препратката към phppos_suppliers
като:
- Не използвате колони от таблицата
- Това е
LEFT JOIN
, което означава, че не изисквате редове да съществуват там.
Преместих GROUP BY
в нова подзаявка, тъй като phppos_sales_item_taxes
е единствената таблица, която може да има дублиращи се редове за дадените критерии. Включих препратката към phppos_sales
защото не съм сигурен дали оптимизаторът на MySQL (или какъвто и да е, наистина) е достатъчно умен, за да изтласка citeria надолу.
Основната част от заявката е преместена в подзаявка просто, за да не се налага да въвеждам формулата за lineSubtotal
много пъти. Използвах едни и същи формули навсякъде, но има налични опростени версии:
Sale_Item.item_unit_price * Sale_Item.quantity_purchased * (1 - (Sale_Item.discount_percent / 100)) as lineSubtotal
Sale_Item.lineSubtotal * COALESCE(Tax.non_cumulative + Tax.cumulative + Tax.non_cumulative * Tax.cumulative, 0) as Tax
.... може да се наложи да ги стартирате чрез счетоводство, тъй като те са склонни да бъдат (разбираемо) докосни относно реда на операциите. Това може води до по-бързо изпълнение, но се съмнявам; най-вече става дума за опростяване на термините до нещо по-четливо.
Не сте предоставили никакви оформления на таблицата за другата половина на заявката, но предполагам, че е подобно. Свързаната модификация е оставена като упражнение за читателя.
Общи стратегии за смекчаване
Освен евентуално ускоряване на промяната на заявката, има редица неща, които можете да направите, за да намалите проблема:
- В слоя на приложението си принудете тази заявка (и евентуално други) да премине през процес на подаване на задание, чиито резултати могат да бъдат извлечени по-късно. Ново копие на тази заявка не може да бъде стартирано, докато предишното не завърши. Предполагам, че php има съществуваща библиотека за това. Като цяло може да се окаже всичко, от което се нуждаете.
- Извличаните данни изглежда подлежат на кеширане - съхранявайте всичко преди най-скоро обработената
sale_date
, а след това получавате нова информация само в движение (въпреки че трансформацията всъщност не е толкова различна от оригинала - обаче, просто да не правите повече свързвания може да помогне). - Забранете заявките през текущия период от време за обработка. Това трябва да предпази системата от опити за достъп до редове, които все още не са заети, и потенциално далеч от индексни страници, които се променят. Този вид трик работи най-добре, ако хранилището ви е подредено така, че да се възползва от едновременния I/O.