Hmm, je pourrais essayer d'écrire votre requête dans le sens suivant :
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
Déplacement de plusieurs colonnes à des fins d'organisation. Cela ne devrait pas avoir d'effet important sur le temps de traitement.
J'ai supprimé la référence à phppos_suppliers
comme :
- Vous n'utilisez aucune colonne du tableau
- C'est une
LEFT JOIN
, ce qui signifie que vous n'avez pas besoin de lignes pour y exister.
J'ai déplacé le GROUP BY
dans une nouvelle sous-requête, car phppos_sales_item_taxes
est la seule table qui pourrait avoir des lignes en double pour les critères donnés. J'ai inclus la référence à phppos_sales
parce que je ne sais pas si l'optimiseur de MySQL (ou tout autre, vraiment) est assez intelligent pour pousser les critères vers le bas.
La partie principale de la requête a été déplacée vers une sous-requête simplement pour que je n'aie pas besoin de taper la formule pour lineSubtotal
plusieurs fois. J'ai utilisé les mêmes formules partout, mais il existe des versions simplifiées :
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
.... vous devrez peut-être les gérer par comptabilité, car ils ont tendance à être (naturellement) sensibles à l'ordre des opérations. Cela peut entraîner une exécution plus rapide mais j'en doute; il s'agit principalement de simplifier les termes en quelque chose de plus lisible.
Vous n'avez fourni aucune disposition de table pour l'autre moitié de la requête, mais je suppose que c'est similaire. La modification associée est laissée en exercice au lecteur.
Stratégies générales d'atténuation
Au-delà de toute accélération potentielle de la modification de la requête, vous pouvez faire un certain nombre de choses pour limiter le problème :
- Dans votre couche d'application, forcez cette requête (et éventuellement d'autres) à passer par un processus de soumission de travail dont les résultats pourront être récupérés ultérieurement. Une nouvelle copie de cette requête ne peut pas être exécutée tant que la précédente n'est pas terminée. Je suppose que php a une bibliothèque existante pour cela. Une simple limitation de la soumission en général peut être tout ce dont vous avez besoin.
- Les données récupérées semblent susceptibles d'être mises en cache :tout est stocké avant la dernière
sale_date
traitée. , puis n'obtenez de nouvelles informations qu'à la volée (bien que la transformation ne soit pas vraiment si différente de l'original - cependant, ne pas faire plus de jointures peut aider). - Interdire les requêtes pendant la durée de traitement actuelle. Cela devrait empêcher le système de tenter d'accéder à des lignes qui n'ont pas encore été validées, et potentiellement loin des pages d'index en cours de modification. Ce type d'astuce fonctionne mieux si votre stockage est conçu pour tirer parti des E/S simultanées.