sql >> Base de Datos >  >> RDS >> Mysql

la consulta costosa derriba el servidor de la base de datos, buscando formas de mitigar

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:

  1. No usa ninguna columna de la tabla
  2. 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:

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