sql >> Database teknologi >  >> RDS >> Mysql

dyr forespørgsel fjerner databaseserveren -- leder efter måder at afbøde

Hmm, jeg kan prøve at skrive din forespørgsel på følgende måde:

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

Flyttede flere kolonner til organisatoriske formål. Dette burde ikke have nogen stor effekt på behandlingstiden.

Jeg fjernede henvisningen til phppos_suppliers som:

  1. Du bruger ingen kolonner fra tabellen
  2. Det er en LEFT JOIN , hvilket betyder, at du ikke kræver, at rækker eksisterer der.

Jeg flyttede GROUP BY ind i en ny underforespørgsel, fordi phppos_sales_item_taxes er den eneste tabel, der kunne have dublerede rækker for de givne kriterier. Jeg inkluderede referencen til phppos_sales fordi jeg ikke er sikker på, om MySQL's optimizer (eller nogen, virkelig) er smart nok til at skubbe citeria ned.

Hoveddelen af ​​forespørgslen er blevet flyttet til en underforespørgsel, så jeg ikke behøver at indtaste formlen for lineSubtotal flere gange. Jeg har brugt de samme formler hele vejen igennem, men der findes forenklede versioner:

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
 

.... du kan dog være nødt til at køre disse efter regnskab, da de har en tendens til (forståeligt nok) at være følsomme med hensyn til rækkefølgen af ​​operationer. Dette kan resultere i en hurtigere runtime, men jeg tvivler på det; for det meste handler det om forenkling af vilkårene til noget mere læsbart.

Du har ikke angivet nogen tabellayout for den anden halvdel af forespørgslen, men jeg formoder, at den ligner. Den relaterede ændring efterlades som en øvelse for læseren.

Generelle afhjælpningsstrategier

Ud over enhver potentiel fremskyndelse, som ændring af forespørgslen måtte have, er der en række ting, du kan gøre for at begrænse problemet:

  1. I dit ansøgningslag skal du tvinge denne forespørgsel (og muligvis andre) til at gennemgå en jobindsendelsesproces, hvis resultater kan hentes senere. En ny kopi af denne forespørgsel kan ikke køres, før den forrige er fuldført. Jeg antager, at php har et eksisterende bibliotek til dette. Blot at begrænse indsendelsen generelt kan være alt, hvad du behøver.
  2. De data, der hentes, ser ud til at kunne gemmes i cache - gem alt før den senest behandlede sale_date , og så kun få ny information på farten (selvom transformationen egentlig ikke er så anderledes fra originalen - dog kan det hjælpe at lade være med at lave flere joins).
  3. Tillad forespørgsler i den aktuelle behandlingstid. Dette skulle forhindre systemet i at forsøge at få adgang til rækker, der endnu ikke er blevet committet, og potentielt væk fra indekssider under modifikation. Denne form for trick fungerer bedst, hvis dit lager er indrettet til at drage fordel af samtidige I/O.



  1. Fejl:PLS-00428:Der forventes en into-klausul i denne select-sætning

  2. Hvorfor bruger SQL Server indeksscanning i stedet for indekssøgning, når WHERE-udtrykket indeholder parametriserede værdier

  3. En tilgang til indeksjustering – del 2

  4. Mysql:Hvordan forespørger man en kolonne, hvis type er bit?