Mysql
 sql >> Database >  >> RDS >> Mysql

una query costosa rimuove il server di database, cercando modi per mitigare

Hmm, potrei provare a scrivere la tua domanda in questo modo:

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 

Spostate diverse colonne per motivi organizzativi. Questo non dovrebbe avere un grande effetto sul tempo di elaborazione.

Ho rimosso il riferimento a phppos_suppliers come:

  1. Non utilizzi colonne della tabella
  2. È un LEFT JOIN , il che significa che non hai bisogno di righe per esistere lì.

Ho spostato il GROUP BY in una nuova sottoquery, perché phppos_sales_item_taxes è l'unica tabella che potrebbe avere righe duplicate per i criteri specificati. Ho incluso il riferimento a phppos_sales perché non sono sicuro che l'ottimizzatore di MySQL (o qualsiasi altro, in realtà) sia abbastanza intelligente da spingere verso il basso i citeria.

La parte principale della query è stata spostata in una sottoquery semplicemente così non avrei bisogno di digitare la formula per lineSubtotal più volte. Ho usato sempre le stesse formule, ma sono disponibili versioni semplificate:

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

.... potresti doverli eseguire con la contabilità, tuttavia, poiché tendono ad essere (comprensibilmente) permalosi sull'ordine delle operazioni. Questo può risultato in un runtime più veloce ma ne dubito; principalmente si tratta di semplificare i termini in qualcosa di più leggibile.

Non hai fornito alcun layout di tabella per l'altra metà della query, ma presumo che sia simile. La relativa modifica è lasciata come esercizio al lettore.

Strategie generali di mitigazione

Al di là di qualsiasi potenziale accelerazione che potrebbe avere la modifica della query, ci sono una serie di cose che potresti fare per ridurre il problema:

  1. Nel tuo livello di applicazione, forza questa query (e possibilmente altre) a passare attraverso un processo di invio del lavoro i cui risultati possono essere recuperati in seguito. Non è possibile eseguire una nuova copia di questa query fino al completamento della precedente. Presumo che php abbia una libreria esistente per questo. La semplice limitazione dell'invio in generale potrebbe essere tutto ciò di cui hai bisogno.
  2. I dati recuperati sembrano suscettibili di memorizzazione nella cache:archivia tutto prima della sale_date elaborata più recente , e quindi ottenere nuove informazioni solo al volo (sebbene la trasformazione non sia proprio così diversa dall'originale, tuttavia, potrebbe essere utile non eseguire più join).
  3. Non consentire query nell'arco di tempo di elaborazione corrente. Ciò dovrebbe impedire al sistema di tentare di accedere a righe di cui non è stato ancora eseguito il commit e potenzialmente lontano dalle pagine di indice in fase di modifica. Questo tipo di trucco funziona meglio se lo spazio di archiviazione è strutturato in modo da sfruttare l'I/O simultaneo.