Ce face SUM() OVER: totaluri cumulate și medii mobile
Orice funcție de agregare (SUM, AVG, COUNT, MIN, MAX, group_concat) devine funcție fereastră dacă îi adaugi OVER (...). Fără ORDER BY în fereastră, calculul se face peste toată partiția: SUM(suma) OVER () este totalul general, afișat pe fiecare rând.
Cu ORDER BY, cadrul implicit este RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: de la primul rând până la rândul curent, inclusiv rândurile cu aceeași valoare de ordonare. Așa obții un total cumulat. Cu ROWS BETWEEN 2 PRECEDING AND CURRENT ROW cadrul are exact ultimele trei rânduri, adică o medie mobilă.
Din SQLite 3.28.0 există și cadrele GROUPS (numără grupuri de valori egale, nu rânduri), clauza EXCLUDE (scoate rândul curent sau egalii lui din cadru) și n PRECEDING cu RANGE. Din 3.30.0, agregările acceptă și FILTER (WHERE ...).
Parametri
| Parametru | Descriere |
|---|---|
ROWS |
Cadrul se măsoară în rânduri fizice. |
RANGE |
Cadrul se măsoară după valoarea din ORDER BY; rândurile egale intră împreună. |
GROUPS |
Cadrul se măsoară în grupuri de rânduri egale (3.28.0+). |
EXCLUDE |
NO OTHERS (implicit), CURRENT ROW, GROUP sau TIES (3.28.0+). |
Exemple
Exemplele de pe această pagină pornesc de la datele de mai jos. Rulează mai întâi acest cod, apoi exemplul.
CREATE TABLE incasari (
zi TEXT NOT NULL,
magazin TEXT NOT NULL,
suma INTEGER NOT NULL
);
INSERT INTO incasari VALUES
('2026-10-19', 'Iași', 500),
('2026-10-20', 'Iași', 700),
('2026-10-21', 'Iași', 300),
('2026-10-22', 'Iași', 900),
('2026-10-19', 'Cluj', 400),
('2026-10-20', 'Cluj', 600),
('2026-10-21', 'Cluj', 800);
Exemplul 1: Total cumulat pe fiecare magazin
PARTITION BY magazin face ca totalul să o ia de la zero pentru Cluj și pentru Iași. Ultima zi a fiecărui magazin arată totalul pe toată perioada.
SELECT magazin, zi, suma,
SUM(suma) OVER (PARTITION BY magazin ORDER BY zi) AS cumulat
FROM incasari
ORDER BY magazin, zi;
Rezultat
╭─────────┬────────────┬──────┬─────────╮
│ magazin │ zi │ suma │ cumulat │
╞═════════╪════════════╪══════╪═════════╡
│ Cluj │ 2026-10-19 │ 400 │ 400 │
│ Cluj │ 2026-10-20 │ 600 │ 1000 │
│ Cluj │ 2026-10-21 │ 800 │ 1800 │
│ Iași │ 2026-10-19 │ 500 │ 500 │
│ Iași │ 2026-10-20 │ 700 │ 1200 │
│ Iași │ 2026-10-21 │ 300 │ 1500 │
│ Iași │ 2026-10-22 │ 900 │ 2400 │
╰─────────┴────────────┴──────┴─────────╯
Exemplul 2: Media mobilă pe ultimele 3 zile
Cadrul ROWS BETWEEN 2 PRECEDING AND CURRENT ROW are cel mult trei rânduri. În primele două zile există mai puține rânduri anterioare, așa că media se face doar pe cele disponibile. round(..., 1) păstrează o zecimală.
SELECT zi, suma,
round(AVG(suma) OVER (ORDER BY zi ROWS BETWEEN 2 PRECEDING AND CURRENT ROW), 1) AS medie_3_zile,
COUNT(*) OVER (ORDER BY zi ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS zile_in_cadru
FROM incasari
WHERE magazin = 'Iași'
ORDER BY zi;
Rezultat
╭────────────┬──────┬──────────────┬───────────────╮
│ zi │ suma │ medie_3_zile │ zile_in_cadru │
╞════════════╪══════╪══════════════╪═══════════════╡
│ 2026-10-19 │ 500 │ 500.0 │ 1 │
│ 2026-10-20 │ 700 │ 600.0 │ 2 │
│ 2026-10-21 │ 300 │ 500.0 │ 3 │
│ 2026-10-22 │ 900 │ 633.3 │ 3 │
╰────────────┴──────┴──────────────┴───────────────╯
Exemplul 3: Procent din total și ziua cea mai bună
OVER () fără nimic în paranteză înseamnă toată partiția, deci totalul magazinului. Înmulțim cu 100.0 ca împărțirea să nu fie între întregi. MAX(suma) OVER (...) arată pe fiecare rând cea mai mare încasare a magazinului.
SELECT magazin, zi, suma,
round(suma * 100.0 / SUM(suma) OVER (PARTITION BY magazin), 1) AS procent,
MAX(suma) OVER (PARTITION BY magazin) AS maxim_magazin
FROM incasari
ORDER BY magazin, zi;
Rezultat
╭─────────┬────────────┬──────┬─────────┬───────────────╮
│ magazin │ zi │ suma │ procent │ maxim_magazin │
╞═════════╪════════════╪══════╪═════════╪═══════════════╡
│ Cluj │ 2026-10-19 │ 400 │ 22.2 │ 800 │
│ Cluj │ 2026-10-20 │ 600 │ 33.3 │ 800 │
│ Cluj │ 2026-10-21 │ 800 │ 44.4 │ 800 │
│ Iași │ 2026-10-19 │ 500 │ 20.8 │ 900 │
│ Iași │ 2026-10-20 │ 700 │ 29.2 │ 900 │
│ Iași │ 2026-10-21 │ 300 │ 12.5 │ 900 │
│ Iași │ 2026-10-22 │ 900 │ 37.5 │ 900 │
╰─────────┴────────────┴──────┴─────────┴───────────────╯
Greșeli frecvente
- Ai valori egale în coloana din
ORDER BYși totalul cumulat „sare”. Cadrul implicitRANGEinclude toate rândurile egale cu cel curent; foloseșteROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWdacă vrei rând cu rând. - Împarți două numere întregi pentru procente:
suma * 100 / totaltaie zecimalele. Scrie100.0ca rezultatul să fie real. - Te aștepți ca
last_value(x) OVER (ORDER BY ...)să dea ultima valoare din partiție. Cu cadrul implicit dă valoarea rândului curent; scrieROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.
Comenzi înrudite
ROW_NUMBER, RANK, LAG (funcții fereastră): Calculează pentru fiecare rând un număr de ordine, un loc în clasament sau valoarea din rândul vecin, fără să grupeze rândurile.WITH (CTE): Dă un nume unei subinterogări (Common Table Expression) și o folosește apoi ca pe un tabel, ca interogarea să fie mai ușor de citit.date, time, datetime: Transformă și calculează date: extrage ziua sau ora, adună zile și luni, găsește începutul lunii sau lunea următoare.