Okenní funkce v SQL: pořadí, průběžné součty a analytika
Published: 2026-03-24 | Stela Augustínová
Okenní funkce 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.
Vzorová 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 okenních funkcí
Každá okenní funkce 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 duplicit.
Příklad: Očíslování všech zaměstnanců podle platu
Příklad: Očíslování zaměstnanců v rámci 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 shodnými hodnotami jinak než ROW_NUMBER():
- ROW_NUMBER() — vždy jedinečné číslo, shody se lámou libovolně
- RANK() — stejná hodnota dostane stejnou pozici, poté se čísla přeskočí (1, 2, 2, 4)
- DENSE_RANK() — stejná hodnota dostane 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 se shodou naloží:
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í okenní funkce
Běžné agregační funkce (SUM, AVG, COUNT, MIN, MAX) lze s OVER použít jako okenní funkce. Na rozdíl od GROUP BY zůstane v výstupu zachován každý řádek.
Příklad: Plat zaměstnance vs. průměr v oddělení
Příklad: Procento z celkových mzdových nákladů
Spočítejte, jakou část celkového payrollu 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 následuje 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: Klouzavý průměr za 2 měsíce
Příklad: Průběžné minimum a maximum tržeb
FIRST_VALUE a LAST_VALUE
FIRST_VALUE(column) vrátí první hodnotu v rámci okenního rámce. LAST_VALUE(column) vrátí 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 okenní funkci 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
Okenní funkce vs. GROUP BY
Klíčový rozdíl: GROUP BY zkolabuje řádky do souhrnných řádků, zatímco okenní funkce každý řádek zachovají a jen přidají vypočtený sloupec.
Příklad: Porovnání vedle sebe
GROUP BY vrátí jeden řádek na oddělení:
Okenní funkce zachovají všechny řádky:
Běžné použití okenních funkcí
- Pořadí a žebříčky — ROW_NUMBER, RANK, DENSE_RANK pro top-N dotazy
- Průběžné součty — kumulativní SUM pro finance a sledování zásob
- Meziobdobá 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 — okenní funkce 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 — okenní funkce 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í
- Okenní funkce 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ů okenních funkcí (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 okenní funkce mohou zjednodušit analytické dotazy!