Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Teure Abfrage legt Datenbankserver lahm -- Suche nach Möglichkeiten zur Eindämmung

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:

  1. Sie verwenden keine Spalten aus der Tabelle
  2. 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:

  1. 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.
  2. 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).
  3. 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.