Hmm, podría intentar escribir su consulta de esta manera:
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
Se movieron varias columnas por motivos organizativos. Esto no debería tener un gran efecto en el tiempo de procesamiento.
Eliminé la referencia a phppos_suppliers
como:
- No usa ninguna columna de la tabla
- Es un
LEFT JOIN
, lo que significa que no necesita filas para existir allí.
Moví el GROUP BY
en una nueva subconsulta, porque phppos_sales_item_taxes
es la única tabla que podría tener filas duplicadas para los criterios dados. Incluí la referencia a phppos_sales
porque no estoy seguro de si el optimizador de MySQL (o cualquier otro, en realidad) es lo suficientemente inteligente como para reducir la citeria.
La parte principal de la consulta se ha movido a una subconsulta simplemente para que no tenga que escribir la fórmula para lineSubtotal
varias veces. He usado las mismas fórmulas en todo momento, pero hay versiones simplificadas disponibles:
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
.... es posible que tenga que ejecutarlos por contabilidad, ya que tienden a ser (comprensiblemente) delicados con respecto al orden de las operaciones. Este puede dar como resultado un tiempo de ejecución más rápido, pero lo dudo; principalmente se trata de la simplificación de los términos a algo más legible.
No proporcionó ningún diseño de tabla para la otra mitad de la consulta, pero supongo que es similar. La modificación relacionada se deja como ejercicio para el lector.
Estrategias generales de mitigación
Más allá de cualquier aceleración potencial que pueda tener el cambio de la consulta, hay una serie de cosas que podría hacer para reducir el problema:
- En su capa de aplicación, fuerce esta consulta (y posiblemente otras) para pasar por un proceso de envío de trabajo cuyos resultados se pueden recuperar más tarde. No se puede ejecutar una nueva copia de esta consulta hasta que se complete la anterior. Supongo que php tiene una biblioteca existente para esto. Simplemente limitar la sumisión en general puede ser todo lo que necesita.
- Los datos que se recuperan parecen susceptibles de almacenamiento en caché:almacene todo antes de la
sale_date
procesada más reciente , y solo obtener nueva información sobre la marcha (aunque la transformación no es realmente tan diferente del original; sin embargo, simplemente no hacer más uniones puede ayudar). - Rechazar consultas durante el tiempo de procesamiento actual. Esto debería evitar que el sistema intente acceder a las filas que aún no se han confirmado y, potencialmente, alejarse de las páginas de índice en proceso de modificación. Este tipo de truco funciona mejor si su almacenamiento está diseñado para aprovechar las E/S simultáneas.