Hmm, jeg kan prøve at skrive din forespørgsel på følgende måde:
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
Flyttede flere kolonner til organisatoriske formål. Dette burde ikke have nogen stor effekt på behandlingstiden.
Jeg fjernede henvisningen til phppos_suppliers
som:
- Du bruger ingen kolonner fra tabellen
- Det er en
LEFT JOIN
, hvilket betyder, at du ikke kræver, at rækker eksisterer der.
Jeg flyttede GROUP BY
ind i en ny underforespørgsel, fordi phppos_sales_item_taxes
er den eneste tabel, der kunne have dublerede rækker for de givne kriterier. Jeg inkluderede referencen til phppos_sales
fordi jeg ikke er sikker på, om MySQL's optimizer (eller nogen, virkelig) er smart nok til at skubbe citeria ned.
Hoveddelen af forespørgslen er blevet flyttet til en underforespørgsel, så jeg ikke behøver at indtaste formlen for lineSubtotal
flere gange. Jeg har brugt de samme formler hele vejen igennem, men der findes forenklede versioner:
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
.... du kan dog være nødt til at køre disse efter regnskab, da de har en tendens til (forståeligt nok) at være følsomme med hensyn til rækkefølgen af operationer. Dette kan resultere i en hurtigere runtime, men jeg tvivler på det; for det meste handler det om forenkling af vilkårene til noget mere læsbart.
Du har ikke angivet nogen tabellayout for den anden halvdel af forespørgslen, men jeg formoder, at den ligner. Den relaterede ændring efterlades som en øvelse for læseren.
Generelle afhjælpningsstrategier
Ud over enhver potentiel fremskyndelse, som ændring af forespørgslen måtte have, er der en række ting, du kan gøre for at begrænse problemet:
- I dit ansøgningslag skal du tvinge denne forespørgsel (og muligvis andre) til at gennemgå en jobindsendelsesproces, hvis resultater kan hentes senere. En ny kopi af denne forespørgsel kan ikke køres, før den forrige er fuldført. Jeg antager, at php har et eksisterende bibliotek til dette. Blot at begrænse indsendelsen generelt kan være alt, hvad du behøver.
- De data, der hentes, ser ud til at kunne gemmes i cache - gem alt før den senest behandlede
sale_date
, og så kun få ny information på farten (selvom transformationen egentlig ikke er så anderledes fra originalen - dog kan det hjælpe at lade være med at lave flere joins). - Tillad forespørgsler i den aktuelle behandlingstid. Dette skulle forhindre systemet i at forsøge at få adgang til rækker, der endnu ikke er blevet committet, og potentielt væk fra indekssider under modifikation. Denne form for trick fungerer bedst, hvis dit lager er indrettet til at drage fordel af samtidige I/O.