Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

consulta cara derruba o servidor de banco de dados - procurando maneiras de mitigar


Hmm, eu poderia tentar escrever sua consulta nestas linhas:
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 

Várias colunas movidas para fins organizacionais. Isso não deve ter grande efeito no tempo de processamento.

Eu removi a referência a phppos_suppliers Como:
  1. Você não usa nenhuma coluna da tabela
  2. É um LEFT JOIN , o que significa que você não precisa que as linhas existam lá.

Eu movi o GROUP BY em uma nova subconsulta, porque phppos_sales_item_taxes é a única tabela que pode ter linhas duplicadas para os critérios fornecidos. Eu incluí a referência a phppos_sales porque não tenho certeza se o otimizador do MySQL (ou qualquer outro, realmente) é inteligente o suficiente para empurrar o citeria para baixo.

A parte principal da consulta foi movida para uma subconsulta simplesmente para que eu não precisasse digitar a fórmula para lineSubtotal várias vezes. Eu usei as mesmas fórmulas por toda parte, mas existem versões simplificadas disponíveis:
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

.... você pode ter que executá-los por contabilidade, no entanto, pois eles tendem a ser (compreensivelmente) melindrosos sobre a ordem das operações. Isso pode resultar em um tempo de execução mais rápido, mas duvido; principalmente trata-se da simplificação dos termos para algo mais legível.

Você não forneceu nenhum layout de tabela para a outra metade da consulta, mas presumo que seja semelhante. A modificação relacionada é deixada como exercício para o leitor.

Estratégias Gerais de Mitigação


Além de qualquer aceleração potencial que a alteração da consulta possa ter, há várias coisas que você pode fazer para reduzir o problema:
  1. Em sua camada de aplicativo, force essa consulta (e possivelmente outras) a passar por um processo de envio de trabalho cujos resultados possam ser recuperados posteriormente. Uma nova cópia desta consulta não pode ser executada até que a anterior seja concluída. Suponho que o php tenha uma biblioteca existente para isso. Simplesmente limitar o envio em geral pode ser tudo o que você precisa.
  2. Os dados que estão sendo recuperados parecem passíveis de armazenamento em cache - armazene tudo antes do sale_date processado mais recente , e só obter novas informações dinamicamente (embora a transformação não seja realmente tão diferente do original - no entanto, simplesmente não fazer mais junções pode ajudar).
  3. Não permita consultas durante o período de processamento atual. Isso deve impedir que o sistema tente acessar as linhas que ainda não foram confirmadas e, potencialmente, longe das páginas de índice sob modificação. Esse tipo de truque funciona melhor se seu armazenamento for projetado para aproveitar a E/S simultânea.