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
Preskúmajte dataset v DbGate Lite

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ície
  • N PRECEDING — N riadkov pred aktuálnym riadkom
  • CURRENT ROW — aktuálny riadok
  • N FOLLOWING — N riadkov za aktuálnym riadkom
  • UNBOUNDED 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

  1. Indexujte stĺpce v ORDER BY — window functions interne triedia dáta; indexy pomáhajú
  2. Minimalizujte počet partícií — veľa malých partícií zvyšuje výpočtovú náročnosť
  3. Znovu používajte definície okien — mnohé databázy podporujú klauzulu WINDOW, aby ste nemuseli opakovať rovnakú špecifikáciu OVER
  4. Filtrujte až po výpočte okien — window functions sa vykonajú pred WHERE, preto na filtrovanie podľa poradia používajte poddotazy alebo CTE
  5. 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 OVER ponechajú 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!