Zanim zaczniemy, wyobraź sobie, że pracujesz z tabelą zawierającą tysiące wierszy sprzedaży. Twoje zadanie: określić, kto z handlowców jest numerem jeden w każdej kategorii, kto jest drugi itd. Albo na przykład musisz ponumerować wszystkie wiersze w zapytaniu, żeby śledzić kolejność. Wszystko to łatwo ogarniesz za pomocą funkcji okiennych.
Funkcje okienne — to funkcje SQL, które działają na podzbiorze wierszy (nazwijmy to "oknem") z zestawu danych. W przeciwieństwie do funkcji agregujących, które łączą wiersze w jeden (np. SUM() albo AVG()), funkcje okienne zostawiają wiersze nietknięte, tylko dodają do nich wyliczone wartości.
Różnica względem funkcji agregujących
Funkcje agregujące "ściskają" dane, grupując wiersze:
SELECT department, COUNT(*)
FROM employees
GROUP BY department;
Wynik: tylko kilka wierszy, po jednym na każdy dział.
Porównajmy to z funkcją okienną — tutaj wiersze zostają na miejscu, ale pojawia się nowe pole, np. ROW_NUMBER():
SELECT employee_name, department,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_within_department
FROM employees;
Dostaniesz te same wiersze, ale z dodatkową kolumną rank_within_department, gdzie każdemu pracownikowi przypisany jest numer w ramach swojego działu.
Podstawowe funkcje okienne
Składnia OVER()
Najważniejsza część każdej funkcji okiennej — to magiczne słowo OVER(). Ono określa, na jakim dokładnie "oknie" danych ta funkcja będzie działać. W środku OVER() możesz ustawić podział na grupy (PARTITION BY) i/lub kolejność sortowania (ORDER BY).
Ogólna składnia:
<funkcja_okienna>() OVER (
[PARTITION BY <grupa>]
[ORDER BY <kolejnosc>]
)
Składniki:
PARTITION BY: Dzieli wiersze na grupy. Na przykład: "podziel dane według działów".ORDER BY: Ustala kolejność przetwarzania wierszy. Na przykład: "sortuj pracowników według pensji od największej do najmniejszej".
Funkcja ROW_NUMBER()
Funkcja ROW_NUMBER() numeruje wiersze, zaczynając od 1, we wskazanym "oknie". Czasem przydaje się do prostego nadania numeru wiersza w tymczasowej tabeli albo do określenia pozycji rekordu.
Przykład. Tabela sales (sprzedaże):
| id | product_category | seller_name | revenue |
|---|---|---|---|
| 1 | Electronics | Alice | 1000 |
| 2 | Electronics | Bob | 850 |
| 3 | Furniture | Alice | 1200 |
| 4 | Furniture | Charlie | 1100 |
| 5 | Electronics | Dana | 750 |
Zapytanie:
SELECT seller_name, product_category, revenue,
ROW_NUMBER() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS row_number
FROM sales;
Wynik:
| seller_name | product_category | revenue | row_number |
|---|---|---|---|
| Alice | Electronics | 1000 | 1 |
| Bob | Electronics | 850 | 2 |
| Dana | Electronics | 750 | 3 |
| Alice | Furniture | 1200 | 1 |
| Charlie | Furniture | 1100 | 2 |
Jak to działa:
- Dane dzielone są na grupy według
product_category. - Każda grupa jest sortowana według
revenue(malejąco). - Wiersze w każdej grupie dostają numer porządkowy.
Funkcja RANK()
Funkcja RANK() służy do rankingowania wierszy. W przeciwieństwie do ROW_NUMBER(), uwzględnia takie same wartości i przeskakuje numery (rangi), jeśli wartości się powtarzają.
Przykład:
SELECT seller_name, product_category, revenue,
RANK() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS rank
FROM sales;
Wynik:
| seller_name | product_category | revenue | rank |
|---|---|---|---|
| Alice | Electronics | 1000 | 1 |
| Bob | Electronics | 850 | 2 |
| Dana | Electronics | 750 | 3 |
| Alice | Furniture | 1200 | 1 |
| Charlie | Furniture | 1100 | 2 |
Funkcja DENSE_RANK()
DENSE_RANK() jest podobna do RANK(), z tą różnicą, że nie przeskakuje numerów rang, jeśli są powtarzające się wartości.
Przykład. Dodajmy sprzedaż z takim samym przychodem:
| id | product_category | seller_name | revenue |
|---|---|---|---|
| 6 | Electronics | Alice | 1000 |
| 7 | Electronics | Dana | 750 |
Zapytanie:
SELECT seller_name, product_category, revenue,
DENSE_RANK() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS dense_rank
FROM sales;
Wynik:
| seller_name | product_category | revenue | dense_rank |
|---|---|---|---|
| Alice | Electronics | 1000 | 1 |
| Alice | Electronics | 1000 | 1 |
| Bob | Electronics | 850 | 2 |
| Dana | Electronics | 750 | 3 |
Przykłady użycia: numerowanie wierszy
Zadanie: ponumerować wszystkie zamówienia w tabeli orders, posortowane według daty.
SELECT order_id, customer_name, order_date,
ROW_NUMBER() OVER (ORDER BY order_date) AS order_number
FROM orders;
Wynik: dostajesz listę zamówień z numeracją według kolejności realizacji.
Przykłady użycia: top-3 sprzedawców w każdej kategorii
Zadanie: określić trzech najlepszych sprzedawców w każdej kategorii produktów.
WITH ranked_sales AS (
SELECT seller_name, product_category, revenue,
RANK() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS rank
FROM sales
)
SELECT seller_name, product_category, revenue
FROM ranked_sales
WHERE rank <= 3;
Przykłady użycia: wykrywanie takich samych wyników
Zadanie: sprawdzić, czy są sprzedawcy z takim samym przychodem w każdej kategorii.
SELECT seller_name, product_category, revenue,
DENSE_RANK() OVER (PARTITION BY product_category ORDER BY revenue DESC) AS dense_rank
FROM sales;
Teraz możesz zobaczyć rangi, gdzie "przyklejają się" takie same wartości.
GO TO FULL VERSION