sql >> Databáze >  >> RDS >> Mysql

drahý dotaz vyřazuje databázový server – hledá způsoby, jak to zmírnit

Hmm, mohl bych zkusit napsat váš dotaz v tomto smyslu:

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 

Přesunuto několik sloupců pro organizační účely. To by nemělo mít žádný velký vliv na dobu zpracování.

Odstranil jsem odkaz na phppos_suppliers jako:

  1. Nepoužíváte žádné sloupce z tabulky
  2. Je to LEFT JOIN , což znamená, že nepotřebujete, aby tam existovaly řádky.

Přesunul jsem GROUP BY do nového poddotazu, protože phppos_sales_item_taxes je jediná tabulka, která může mít duplicitní řádky pro daná kritéria. Vložil jsem odkaz na phppos_sales protože si nejsem jistý, jestli je optimalizátor MySQL (nebo jiný, opravdu) dost chytrý, aby stlačil citeria dolů.

Hlavní část dotazu byla jednoduše přesunuta do poddotazu, takže bych nemusel zadávat vzorec pro lineSubtotal vícekrát. Použil jsem stejné vzorce, ale jsou k dispozici zjednodušené verze:

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

.... možná je však budete muset vést účetnictvím, protože bývají (pochopitelně) citlivé na pořadí operací. To může výsledkem je rychlejší běh, ale pochybuji o tom; většinou jde o zjednodušení pojmů na něco čitelnějšího.

Neposkytli jste žádné rozložení tabulek pro druhou polovinu dotazu, ale předpokládám, že je to podobné. Související modifikace je ponechána jako cvičení pro čtenáře.

Obecné zmírňující strategie

Kromě potenciálního zrychlení, které může změna dotazu přinést, existuje řada věcí, které můžete udělat pro omezení problému:

  1. Ve své aplikační vrstvě vynuťte, aby tento dotaz (a možná i další) prošel procesem odeslání úlohy, jehož výsledky lze načíst později. Novou kopii tohoto dotazu nelze spustit, dokud se nedokončí předchozí. Předpokládám, že php má na to existující knihovnu. Vše, co potřebujete, může být obecně pouze omezení odeslání.
  2. Zdá se, že načítaná data lze uložit do mezipaměti – uložte vše před posledním zpracováním sale_date a pak teprve získávat nové informace za běhu (ačkoli transformace ve skutečnosti není tak odlišná z originálu – může však pomoci jednoduše neprovádět více spojení).
  3. Zakázat dotazy během aktuální doby zpracování. To by mělo zabránit systému v pokusech o přístup k řádkům, které ještě nebyly potvrzeny, a potenciálně mimo indexové stránky, které se upravují. Tento druh triku funguje nejlépe, pokud je vaše úložiště uspořádáno tak, aby využívalo výhody souběžných I/O.



  1. Kde klauzule funguje, nefunguje na tinyint

  2. Jak MAKETIME() funguje v MariaDB

  3. Jak vygeneruji SQL skript z mého diagramu v MySQL Workbench?

  4. Porozumění připraveným prohlášením CHOP a závazným parametrům