SQL window functions: poradie, priebežné súčty a analytika
Published: 2026-03-24
Window functions vykonávajú výpočty nad množinou riadkov, ktoré súvisia s aktuálnym riadkom — bez toho, aby zoskupili výsledok do jedného riadku ako GROUP BY. Sú jednou z najsilnejších funkcií moderného SQL, podporované v SQLite (3.25+), PostgreSQL, MySQL (8.0+), SQL Serveri a Oracle.
Vzorové dáta
Vo všetkých príkladoch používame dve tabuľky:
Tabuľka employees:
| id | name | department | hire_date | salary |
|---|---|---|---|---|
| 1 | Alice | Engineering | 2023-01-15 | 95000 |
| 2 | Bob | Engineering | 2023-06-01 | 85000 |
| 3 | Carol | Engineering | 2024-03-10 | 85000 |
| 4 | Dave | Sales | 2023-02-20 | 72000 |
| 5 | Eve | Sales | 2023-09-05 | 68000 |
| 6 | Frank | Sales | 2024-01-15 | 65000 |
| 7 | Grace | Marketing | 2023-04-01 | 82000 |
| 8 | Henry | Marketing | 2024-06-01 | 70000 |
Tabuľka monthly_sales:
| id | employee_id | month | revenue |
|---|---|---|---|
| 1 | 1 | 2026-01 | 22000 |
| 2 | 1 | 2026-02 | 25000 |
| 3 | 1 | 2026-03 | 28000 |
| 4 | 2 | 2026-01 | 19000 |
| 5 | 2 | 2026-02 | 21000 |
| 6 | 2 | 2026-03 | 20000 |
| 7 | 3 | 2026-01 | 15000 |
| 8 | 3 | 2026-02 | 17000 |
| 9 | 3 | 2026-03 | 19000 |
| 10 | 4 | 2026-01 | 18000 |
| 11 | 4 | 2026-02 | 22000 |
| 12 | 4 | 2026-03 | 17000 |
| 13 | 5 | 2026-01 | 14000 |
| 14 | 5 | 2026-02 | 16000 |
| 15 | 5 | 2026-03 | 19000 |
| 16 | 6 | 2026-01 | 11000 |
| 17 | 6 | 2026-02 | 13000 |
| 18 | 6 | 2026-03 | 15000 |
| 19 | 7 | 2026-01 | 9000 |
| 20 | 7 | 2026-02 | 12000 |
| 21 | 7 | 2026-03 | 14000 |
| 22 | 8 | 2026-01 | 7000 |
| 23 | 8 | 2026-02 | 9000 |
| 24 | 8 | 2026-03 | 11000 |
Syntax window functions
Každá okenná funkcia používa klauzulu OVER na definovanie toho, nad ktorými riadkami bude pracovať:
function_name(...) OVER (
[PARTITION BY column]
[ORDER BY column]
[frame_clause]
)
- PARTITION BY — rozdelí riadky do skupín (podobne ako GROUP BY, ale ponechá všetky riadky)
- ORDER BY — určuje poradie riadkov v rámci každej partície
- Frame clause — obmedzí okno na rozsah riadkov relatívne k aktuálnemu riadku
ROW_NUMBER()
ROW_NUMBER() priradí každému riadku v rámci partície jedinečné postupné číslo. Číslovanie vždy začína od 1, bez medzier a bez zhodných hodnôt.
Príklad: Očíslovanie všetkých zamestnancov podľa platu
Príklad: Očíslovanie zamestnancov v rámci každého oddelenia
Pomocou PARTITION BY sa číslovanie pre každé oddelenie začína od 1:
RANK() a DENSE_RANK()
RANK() a DENSE_RANK() pracujú s remízami inak ako ROW_NUMBER():
- ROW_NUMBER() — vždy jedinečné číslo, remízy sa lámu ľubovoľne
- RANK() — rovnaká hodnota dostane rovnaké poradie, potom sa čísla preskočia (1, 2, 2, 4)
- DENSE_RANK() — rovnaká hodnota dostane rovnaké poradie, bez medzier (1, 2, 2, 3)
Príklad: Porovnanie všetkých troch funkcií na radenie
Bob a Carol zarábajú 85000 — všimnite si, ako každá funkcia spracuje remízu:
Príklad: Poradie zamestnancov v rámci oddelenia
NTILE()
NTILE(n) rozdelí riadky do n približne rovnako veľkých skupín (bucketov), očíslovaných od 1 po n.
Príklad: Rozdelenie zamestnancov do platových kvartilov
Príklad: Rozdelenie zamestnancov v oddelení do dvoch úrovní
LAG() a LEAD()
LAG(column, offset) pristupuje k hodnote z predchádzajúceho riadku. LEAD(column, offset) pristupuje k hodnote z nasledujúceho riadku. Obe funkcie majú predvolený offset 1 a vracajú NULL, ak taký riadok neexistuje.
Príklad: Medzimesačné porovnanie tržieb
Porovnajte tržby v každom mesiaci s predchádzajúcim mesiacom pre každého zamestnanca:
Príklad: Náhľad tržieb v nasledujúcom mesiaci
Agregačné window functions
Štandardné agregačné funkcie (SUM, AVG, COUNT, MIN, MAX) je možné použiť ako window functions pomocou OVER. Na rozdiel od GROUP BY zostane vo výstupe zachovaný každý riadok.
Príklad: Plat každého zamestnanca vs. priemer oddelenia
Príklad: Percento z celkového objemu miezd
Vypočítajte, aký podiel na celkových mzdových nákladoch predstavuje každý zamestnanec:
Príklad: Priebežný súčet a kumulatívny počet
Priebežné súčty podľa partície
Skombinujte PARTITION BY a ORDER BY na výpočet priebežných súčtov v rámci skupín.
Príklad: Kumulatívne tržby na zamestnanca
Príklad: Mesačné poradie tržieb naprieč všetkými zamestnancami
Zistite, ako sa každý zamestnanec umiestnil v jednotlivých mesiacoch:
Okenné rámce (window frames)
Okenný rámec presne určuje, ktoré riadky relatívne k aktuálnemu riadku sú zahrnuté do výpočtu. Rámec sa uvádza za ORDER BY vnútri OVER.
Syntax
ROWS BETWEEN <start> AND <end>
Bežné hranice:
UNBOUNDED PRECEDING— od prvého riadku partícieN PRECEDING— N riadkov pred aktuálnym riadkomCURRENT ROW— aktuálny riadokN FOLLOWING— N riadkov za aktuálnym riadkomUNBOUNDED FOLLOWING— po posledný riadok partície
Príklad: 2-mesačný kĺzavý priemer
Príklad: Priebežné minimum a maximum tržieb
FIRST_VALUE a LAST_VALUE
FIRST_VALUE(column) vráti prvú hodnotu v okennom rámci. LAST_VALUE(column) vráti poslednú hodnotu. Pozor: LAST_VALUE s predvoleným rámcom (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) vidí iba po aktuálny riadok.
Príklad: Porovnanie každého mesiaca s prvým mesiacom
Príklad: Porovnanie každého mesiaca s najlepším mesiacom
Použite ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING, aby ste videli celú partíciu:
Praktický príklad: Najlepšie zarábajúci v oddelení
Bežný vzor: použite window function v poddotaze a potom filtrujte podľa poradia.
Príklad: Najlepšie platený zamestnanec v každom oddelení
Príklad: Mesiac s najvyššími tržbami pre každého zamestnanca
Window functions vs. GROUP BY
Kľúčový rozdiel: GROUP BY zoskupí riadky do súhrnných riadkov, zatiaľ čo window functions ponechajú každý riadok a pridajú vypočítaný stĺpec.
Príklad: Porovnanie vedľa seba
GROUP BY vráti jeden riadok na oddelenie:
Window functions ponechajú všetky riadky:
Bežné použitia window functions
- Poradie a rebríčky — ROW_NUMBER, RANK, DENSE_RANK pre top-N dotazy
- Priebežné súčty — kumulatívne SUM pre finančné a skladové sledovanie
- Medziobdobové porovnania — LAG/LEAD pre medzimesačnú alebo medziročnú analýzu
- Kĺzavé priemery — vyhladzovanie dát pomocou rámcovej AVG
- Percentilová analýza — NTILE na rozdelenie dát do rovnako veľkých skupín
- Deduplikácia — ROW_NUMBER na výber jedného riadku z každej skupiny
- Analýza medzier — LAG na detekciu chýbajúcich sekvencií alebo nezvyčajných zmien
Tipy k výkonu
- Indexujte stĺpce v ORDER BY — window functions interne triedia dáta; indexy pomáhajú
- Minimalizujte počet partícií — veľa malých partícií zvyšuje výpočtovú náročnosť
- Znovu používajte definície okien — mnohé databázy podporujú klauzulu
WINDOW, aby ste nemuseli opakovať rovnakú špecifikáciuOVER - Filtrujte až po výpočte okien — window functions sa vykonajú pred
WHERE, preto na filtrovanie podľa poradia používajte poddotazy alebo CTE - Vyhýbajte sa zbytočným rámcom — rámec špecifikujte len vtedy, keď potrebujete neštandardné správanie
Zhrnutie
- Window functions počítajú hodnoty naprieč súvisiacimi riadkami bez zoskupenia výslednej množiny
- ROW_NUMBER, RANK, DENSE_RANK priraďujú riadkom poradové čísla
- LAG a LEAD pristupujú k hodnotám z predchádzajúcich alebo nasledujúcich riadkov
- NTILE rozdeľuje riadky do rovnako veľkých bucketov
- FIRST_VALUE a LAST_VALUE vracajú hraničné hodnoty z okna
- Agregačné funkcie (SUM, AVG, COUNT, MIN, MAX) s
OVERponechajú všetky riadky a zároveň počítajú metriky na úrovni skupín - PARTITION BY vytvára nezávislé skupiny; ORDER BY určuje poradie riadkov
- Okenné rámce (
ROWS BETWEEN ... AND ...) jemne doladia, ktoré riadky sú zahrnuté - Na filtrovanie podľa výsledkov window functions (napr. top-N v skupine) používajte poddotazy
- Podporované v SQLite 3.25+, PostgreSQL, MySQL 8.0+, SQL Serveri a Oracle
Skúste upraviť príklady, experimentovať s rôznymi partíciami, zoradeniami a rámcami, aby ste videli, ako window functions dokážu zjednodušiť vaše analytické dotazy!