SQL window functions: pořadí, průběžné součty a analytika
Published: 2026-03-24
Window functions provádějí výpočty nad množinou řádků, které souvisejí s aktuálním řádkem — aniž by výsledek zkolabovaly do jednoho řádku jako GROUP BY. Patří mezi nejmocnější funkce v moderním SQL a jsou podporovány v SQLite (3.25+), PostgreSQL, MySQL (8.0+), SQL Serveru a Oracle.
Ukázková data
Ve všech příkladech používáme dvě tabulky:
Tabulka 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 |
Tabulka 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 |
Syntaxe window functions
Každá window function používá klauzuli OVER, která určuje, nad kterými řádky se má výpočet provádět:
function_name(...) OVER (
[PARTITION BY column]
[ORDER BY column]
[frame_clause]
)
- PARTITION BY — rozdělí řádky do skupin (podobně jako GROUP BY, ale všechny řádky zachová)
- ORDER BY — určuje pořadí řádků v rámci každé partition
- Frame clause — omezuje okno na rozsah řádků relativně k aktuálnímu řádku
ROW_NUMBER()
ROW_NUMBER() přiřadí každému řádku v rámci partition jedinečné pořadové číslo. Číslování vždy začíná od 1, bez mezer a bez shodných hodnot.
Příklad: Očíslování všech zaměstnanců podle platu
Příklad: Očíslování zaměstnanců v rámci každého oddělení
Pomocí PARTITION BY se číslování pro každé oddělení znovu začíná od 1:
RANK() a DENSE_RANK()
RANK() a DENSE_RANK() pracují s remízami jinak než ROW_NUMBER():
- ROW_NUMBER() — vždy jedinečné číslo, remízy se lámou libovolně
- RANK() — stejné hodnoty dostanou stejnou pozici, poté se čísla přeskočí (1, 2, 2, 4)
- DENSE_RANK() — stejné hodnoty dostanou stejnou pozici, bez mezer (1, 2, 2, 3)
Příklad: Porovnání všech tří funkcí pro pořadí
Bob a Carol mají oba plat 85000 — všimněte si, jak každá funkce remízu zpracuje:
Příklad: Pořadí zaměstnanců v rámci oddělení
NTILE()
NTILE(n) rozdělí řádky do n přibližně stejně velkých skupin (bucketů), očíslovaných od 1 do n.
Příklad: Rozdělení zaměstnanců do platových kvartilů
Příklad: Rozdělení zaměstnanců v oddělení do dvou úrovní
LAG() a LEAD()
LAG(column, offset) zpřístupní hodnotu z předchozího řádku. LEAD(column, offset) zpřístupní hodnotu z následujícího řádku. Obě funkce mají výchozí offset 1 a vracejí NULL, pokud takový řádek neexistuje.
Příklad: Meziměsíční porovnání tržeb
Porovnejte tržby v každém měsíci s předchozím měsícem pro každého zaměstnance:
Příklad: Náhled tržeb v příštím měsíci
Agregační window functions
Běžné agregační funkce (SUM, AVG, COUNT, MIN, MAX) lze s OVER použít jako window functions. Na rozdíl od GROUP BY zůstane ve výstupu zachován každý řádek.
Příklad: Plat zaměstnance vs. průměr oddělení
Příklad: Procento z celkových mzdových nákladů
Spočítejte, jaký podíl na celkové mzdové náročnosti představuje každý zaměstnanec:
Příklad: Průběžný součet a kumulativní počet
Průběžné součty podle partition
Kombinací PARTITION BY a ORDER BY můžete počítat průběžné součty v rámci skupin.
Příklad: Kumulativní tržby na zaměstnance
Příklad: Měsíční pořadí tržeb napříč všemi zaměstnanci
Zjistěte, jak si každý zaměstnanec vedl v jednotlivých měsících:
Okenní rámce (window frames)
Okenní rámec přesně určuje, které řádky relativně k aktuálnímu řádku jsou do výpočtu zahrnuty. Klauzule rámce se píše za ORDER BY uvnitř OVER.
Syntaxe
ROWS BETWEEN <start> AND <end>
Běžné hranice:
UNBOUNDED PRECEDING— od prvního řádku partitionN PRECEDING— N řádků před aktuálním řádkemCURRENT ROW— aktuální řádekN FOLLOWING— N řádků za aktuálním řádkemUNBOUNDED FOLLOWING— až po poslední řádek partition
Příklad: 2měsíční klouzavý průměr
Příklad: Průběžné minimum a maximum tržeb
FIRST_VALUE a LAST_VALUE
FIRST_VALUE(column) vrací první hodnotu v rámci okenního rámce. LAST_VALUE(column) vrací poslední hodnotu. Pozor: LAST_VALUE s výchozím rámcem (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) vidí jen do aktuálního řádku.
Příklad: Porovnání každého měsíce s prvním měsícem
Příklad: Porovnání každého měsíce s nejlepším měsícem
Použijte ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING, abyste viděli celou partition:
Praktický příklad: Nejlépe placený v oddělení
Častý vzor: použijte window function v poddotazu a pak filtrujte podle pořadí.
Příklad: Nejlépe placený zaměstnanec v každém oddělení
Příklad: Měsíc s nejvyššími tržbami pro každého zaměstnance
Window functions vs. GROUP BY
Klíčový rozdíl: GROUP BY zkolabuje řádky do souhrnných řádků, zatímco window functions všechny řádky zachovají a jen přidají vypočtený sloupec.
Příklad: Porovnání vedle sebe
GROUP BY vrací jeden řádek na oddělení:
Window functions zachovají všechny řádky:
Běžné použití window functions
- Pořadí a žebříčky — ROW_NUMBER, RANK, DENSE_RANK pro dotazy typu top-N
- Průběžné součty — kumulativní SUM pro finance a sledování zásob
- Meziobdobová srovnání — LAG/LEAD pro meziměsíční nebo meziroční analýzu
- Klouzavé průměry — vyhlazování dat pomocí AVG nad rámcem
- Percentilová analýza — NTILE pro rozdělení dat do stejně velkých skupin
- Deduplikace — ROW_NUMBER pro výběr jednoho řádku ze skupiny
- Analýza mezer — LAG pro detekci chybějících sekvencí nebo neobvyklých změn
Tipy k výkonu
- Indexujte sloupce v ORDER BY — window functions interně třídí data; indexy pomáhají
- Minimalizujte počet partition — velké množství partition zvyšuje výpočetní náročnost
- Znovu používejte definice oken — mnoho databází podporuje klauzuli
WINDOW, abyste nemuseli opakovat stejnou specifikaciOVER - Filtrujte až po výpočtu oken — window functions se vyhodnocují před
WHERE, proto pro filtrování podle pořadí používejte poddotazy nebo CTE - Vyhněte se zbytečným rámcům — rámec specifikujte jen tehdy, když potřebujete jiné chování než výchozí
Shrnutí
- Window functions počítají hodnoty napříč souvisejícími řádky bez zkolabování výsledné množiny
- ROW_NUMBER, RANK, DENSE_RANK přiřazují řádkům pořadí
- LAG a LEAD zpřístupňují hodnoty z předchozích nebo následujících řádků
- NTILE rozděluje řádky do stejně velkých bucketů
- FIRST_VALUE a LAST_VALUE vracejí hraniční hodnoty z okna
- Agregační funkce (SUM, AVG, COUNT, MIN, MAX) s
OVERzachovají všechny řádky a zároveň počítají metriky na úrovni skupin - PARTITION BY vytváří nezávislé skupiny; ORDER BY určuje pořadí řádků
- Okenní rámce (
ROWS BETWEEN ... AND ...) jemně doladí, které řádky jsou zahrnuty - Pro filtrování podle výsledků window functions (např. top-N ve skupině) používejte poddotazy
- Podporováno v SQLite 3.25+, PostgreSQL, MySQL 8.0+, SQL Serveru a Oracle
Zkuste si příklady upravit, experimentujte s různými partition, řazením a rámci a uvidíte, jak vám window functions mohou zjednodušit analytické dotazy!