Mysql
 sql >> Base de données >  >> RDS >> Mysql

une requête coûteuse supprime le serveur de base de données - recherche de moyens d'atténuation

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 :

  1. Vous n'utilisez aucune colonne du tableau
  2. 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 :

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