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:- Você não usa nenhuma coluna da tabela
- É 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:
- 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.
- 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). - 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.