Hmm, ich könnte versuchen, Ihre Abfrage wie folgt zu schreiben:
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
Mehrere Spalten aus organisatorischen Gründen verschoben. Dies sollte keine großen Auswirkungen auf die Verarbeitungszeit haben.
Ich habe den Verweis auf phppos_suppliers
entfernt als:
- Sie verwenden keine Spalten aus der Tabelle
- Es ist ein
LEFT JOIN
, was bedeutet, dass dort keine Zeilen vorhanden sein müssen.
Ich habe GROUP BY
verschoben in eine neue Unterabfrage, weil phppos_sales_item_taxes
ist die einzige Tabelle, die doppelte Zeilen für die angegebenen Kriterien haben könnte. Ich habe den Verweis auf phppos_sales
eingefügt weil ich mir nicht sicher bin, ob der Optimierer von MySQL (oder irgendein anderer wirklich) schlau genug ist, um Kriterien nach unten zu drücken.
Der Hauptteil der Abfrage wurde einfach in eine Unterabfrage verschoben, damit ich die Formel für lineSubtotal
nicht eingeben muss mehrmals. Ich habe durchgehend die gleichen Formeln verwendet, aber es gibt vereinfachte Versionen:
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
.... Sie müssen diese jedoch möglicherweise nach Buchhaltung ausführen, da sie dazu neigen, (verständlicherweise) empfindlich in Bezug auf die Reihenfolge der Vorgänge zu sein. Dies kann führt zu einer schnelleren Laufzeit, aber ich bezweifle es; Meistens geht es um die Vereinfachung der Begriffe zu etwas Lesbarerem.
Sie haben keine Tabellenlayouts für die andere Hälfte der Abfrage bereitgestellt, aber ich nehme an, es ist ähnlich. Die entsprechende Modifikation bleibt dem Leser als Übung überlassen.
Allgemeine Minderungsstrategien
Abgesehen von der potenziellen Beschleunigung, die eine Änderung der Abfrage haben könnte, gibt es eine Reihe von Dingen, die Sie tun könnten, um das Problem einzudämmen:
- Erzwingen Sie in Ihrer Anwendungsschicht, dass diese Abfrage (und möglicherweise andere) einen Jobübermittlungsprozess durchläuft, dessen Ergebnisse später abgerufen werden können. Eine neue Kopie dieser Abfrage kann erst ausgeführt werden, wenn die vorherige abgeschlossen ist. Ich gehe davon aus, dass PHP dafür eine vorhandene Bibliothek hat. Einfache Drosselung der Übermittlung im Allgemeinen kann alles sein, was Sie brauchen.
- Die abgerufenen Daten scheinen für das Caching geeignet zu sein - speichern Sie alles vor dem zuletzt verarbeiteten
sale_date
, und erhalten dann nur spontan neue Informationen (obwohl die Transformation nicht wirklich so anders ist vom Original - es kann jedoch hilfreich sein, einfach keine weiteren Joins zu erstellen). - Abfragen über den aktuellen Verarbeitungszeitraum nicht zulassen. Dies sollte das System davon abhalten, auf Zeilen zuzugreifen, die noch nicht festgeschrieben wurden, und möglicherweise von Indexseiten entfernt, die gerade geändert werden. Diese Art von Trick funktioniert am besten, wenn Ihr Speicher so ausgelegt ist, dass er gleichzeitige E/A nutzt.