sql >> Database >  >> RDS >> Mysql

Cumulatieve som over een reeks rijen in mysql

UPDATE

MySQL 8.0 introduceert "vensterfuncties", functionaliteit gelijk aan SQL Server "vensterfuncties" (met partitionering en volgorde door Transact-SQL OVER syntaxis) en Oracle "analytische functies".

MySQL Reference Manual 12.21 Vensterfuncties https://dev.mysql .com/doc/refman/8.0/en/window-functions.html

Het hier gegeven antwoord is een benadering voor MySQL-versies ouder dan 8.0.

ORIGINEEL ANTWOORD

MySQL biedt niet het type analytische functie die u zou gebruiken om een ​​lopende "cumulatieve som" te krijgen, zoals de analytische functies die beschikbaar zijn in andere DBMS (zoals Oracle of SQL Server.)

Maar het is mogelijk om sommige analytische functies te emuleren met MySQL.

Er zijn (minstens) twee werkbare benaderingen:

Een daarvan is om een ​​gecorreleerde subquery te gebruiken om het subtotaal te krijgen. Deze benadering kan duur zijn voor grote sets en gecompliceerd als de predikaten op de buitenste query gecompliceerd zijn. Het hangt er echt van af hoe ingewikkeld die "meerdere joins op meerdere tabellen" zijn. (Helaas ondersteunt MySQL ook geen CTE's.)

De andere benadering is om gebruik te maken van MySQL-gebruikersvariabelen om wat controleonderbrekingen te verwerken. De "truc" hier is om de resultaten van uw zoekopdracht te sorteren (met behulp van ORDER BY) en uw zoekopdracht vervolgens in een andere zoekopdracht te plaatsen.

Ik zal een voorbeeld geven van de laatste benadering.

Vanwege de volgorde waarin MySQL bewerkingen uitvoert, is de cumulative_total kolom moet worden berekend vóór de waarde van id en day uit de huidige rij worden opgeslagen in gebruikersvariabelen. Het is gewoon het gemakkelijkst om deze kolom als eerste te plaatsen.

De inline-weergave met een alias als i (in de onderstaande query) is er alleen om de gebruikersvariabelen te initialiseren, voor het geval deze al in de sessie zijn ingesteld. Als hieraan al waarden zijn toegewezen, willen we hun huidige waarden negeren, en de eenvoudigste manier om dat te doen is door ze te initialiseren.

Uw oorspronkelijke zoekopdracht wordt tussen haakjes geplaatst en krijgt een alias, c in het onderstaande voorbeeld. De enige wijziging aan uw oorspronkelijke zoekopdracht is de toevoeging van een ORDER BY-clausule, zodat we er zeker van kunnen zijn dat we de rijen uit de zoekopdracht in volgorde verwerken.

De buitenste select controleert of de id en day waarde uit de huidige rij "komt overeen" met de vorige rij. Als ze dat doen, voegen we het amount toe van de huidige rij naar het cumulatieve subtotaal. Als ze niet overeenkomen, stellen we het cumulatieve subtotaal opnieuw in op nul en voegen we het bedrag uit de huidige rij toe (of, eenvoudiger, wijzen we het bedrag toe uit de huidige rij).

Nadat we de berekening van het cumulatieve totaal hebben gedaan, bewaren we de id en day waarden van de huidige rij in gebruikersvariabelen, zodat ze beschikbaar zijn wanneer we de volgende rij verwerken.

Bijvoorbeeld:

SELECT IF(@prev_id = c.id AND @prev_day = c.day
         ,@cumtotal := @cumtotal + c.amount
         ,@cumtotal := c.amount) AS cumulative_total
     , @prev_id  := c.id  AS `id`
     , @prev_day := c.day AS `day`
     , c.hr
     , c.amount AS `amount'
  FROM ( SELECT @prev_id  := NULL
              , @prev_day := NULL
              , @subtotal := 0
       ) i
  JOIN (

         select id, day, hr, amount from
         ( //multiple joins on multiple tables)a
         left join
         (//unions on multiple tables)b
         on a.id=b.id

         ORDER BY 1,2,3
       ) c

Als het nodig is om de kolommen in een andere volgorde te retourneren, met cumulatief totaal als laatste kolom, dan is een optie om die hele instructie tussen haakjes te plaatsen en die query als een inline-weergave te gebruiken:

SELECT d.id
     , d.day
     , d.hr
     , d.amount
     , d.cumulative_total
FROM (
       // query from above
     ) d


  1. Apache Spark ODBC-stuurprogramma

  2. Wijzig decimaalteken in MySQL

  3. Kan ik een weergave maken met een parameter in MySQL?

  4. oracle sql:update indien bestaat, anders invoegen