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

Hvordan man laver en rullende sum, skal hver række inkludere summen af ​​tidligere rækker

Du kan gøre brug af MySQL-brugervariabler til at efterligne analytiske funktioner. (Der er også nogle andre tilgange, som f.eks. at bruge en semi-join eller at bruge en korreleret underforespørgsel. Jeg kan også levere løsninger til dem, hvis du føler, de kan være mere passende.)

For at efterligne en "running total" analytisk funktion, prøv noget som dette:

SELECT t.user_id
     , t.starttime
     , t.order_number
     , IF(t.order_number IS NOT NULL,
         @tot_dur := 0,
         @tot_dur := @tot_dur + t.visit_duration_seconds) AS tot_dur
  FROM visit t
  JOIN (SELECT @tot_dur := 0) d
 ORDER BY t.user_id, t.start_time

"Tricket" her er at bruge en IF-funktion til at teste om order_number er nul. Når den er nul, tilføjer vi varighedsværdien til variablen, ellers sætter vi variablen til nul.

Vi bruger en indlejret visning (aliasset som d , for at sikre, at @tot_dur-variablen initialiseres til nul.

BEMÆRK:Vær forsigtig med at bruge MySQL-brugervariabler som denne. I SELECT-sætningen som ovenfor sker tildelingen af ​​variablerne i SELECT-listen efter ORDER BY, så vi kan få deterministisk adfærd.

Den forespørgsel håndterer ikke "pauser" i user_id. For at få det skal vi bruge værdien af ​​user_id fra den forrige række. Vi kan bevare det i en anden brugervariabel. Rækkefølgen af ​​operationerne er deterministisk, og vi skal passe på med at lave akkumuleringen, FØR vi overskriver user_id'et fra den forrige række.

Vi skal enten omarrangere kolonnerne, så user_id vises efter tot_dur (eller inkludere en anden kopi af user_id-kolonnen)

SELECT t.user_id
     , t.starttime
     , t.order_number
     , IF(t.order_number IS NULL,
         @tot_dur := IF(@prev_user_id = t.user_id,@tot_dur,0) + t.visit_duration_seconds,
         @tot_dur := 0
       ) AS tot_dur
     , @prev_user_id := t.user_id AS prev_user_id
  FROM visit t
  JOIN (SELECT @tot_dur := 0, @prev_user_id := NULL) d
 ORDER BY t.user_id, t.start_time

Værdierne returneret i user_id og prev_user_id kolonner er identiske. Den "ekstra" kolonne kunne fjernes, eller kolonnerne kunne omarrangeres ved at ombryde forespørgslen (som en indlejret visning) i en anden forespørgsel, selvom dette koster en ydeevne:

SELECT v.user_id
     , v.starttime
     , v.order_number
     , v.tot_dur
  FROM (SELECT t.starttime
             , t.order_number
             , IF(t.order_number IS NULL,
                 @tot_dur := IF(@prev_user_id = t.user_id,@tot_dur,0) + t.visit_duration_seconds,
                 @tot_dur := 0
               ) AS tot_dur
             , @prev_user_id := t.user_id AS user_id
          FROM visit t
          JOIN (SELECT @tot_dur := 0, @prev_user_id := NULL) d
         ORDER BY t.user_id, t.start_time
       ) v

Denne forespørgsel viser, at det er muligt for MySQL at returnere det angivne resultatsæt. Men for optimal ydeevne vil vi kun køre forespørgslen i den indbyggede visning (aliasset som v ), og håndtere ombestillingen af ​​kolonnerne (sætte bruger_id-kolonnen først) på klientsiden, når rækkerne er hentet.

De to andre almindelige tilgange er at bruge en semi-join og en korreleret underforespørgsel, selvom disse tilgange kan være mere ressourcekrævende, når man behandler store sæt.




  1. Er det muligt at kalde lagret procedure i udsigt?

  2. Hvad er det bedste DBMS til jobbet?

  3. Strengsammenkædning i MySQL

  4. Python 3.4.0 med MySQL-database