На перший погляд, window-функції та агрегатні функції здаються схожими інструментами для аналізу та обробки даних. Бо і ті, і інші роблять обчислення, такі як сума, середнє, ранжування і т.д. Але давай розберемося, чим вони реально відрізняються.
Агрегатні функції (GROUP BY)
Агрегатні функції працюють так:
- Вони групують рядки по вказаних стовпцях.
- Після групування кожна група перетворюється на один рядок результату.
- Приклад: ти хочеш дізнатися сумарний дохід по кожному регіону.
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
Особливість: GROUP BY "стискає" дані. Якщо ти юзаєш групування, то всі рядки, що входять в одну групу, зникають — залишається тільки результат агрегації.
Window-функції (PARTITION BY)
Window-функції, навпаки:
- Зберігають початкову структуру даних (ніяких стискань чи зникнень рядків!).
- Можуть робити обчислення всередині "вікон" — логічно відокремлених груп рядків.
Приклад: ти хочеш дізнатися долю продажів кожного міста в сумарних продажах його регіону, але при цьому зберегти всі дані.
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales_data;
Особливість: використання window-функцій не видаляє рядки, а просто додає нові обчислювані значення до кожного рядка.
Приклад: SUM() з GROUP BY vs SUM() з PARTITION BY
Щоб краще зрозуміти різницю, давай подивимось, як SUM() працює в обох випадках. Уявімо, що у нас є таблиця sales_data такого вигляду:
| region | city | sales |
|---|---|---|
| North | CityA | 100 |
| North | CityB | 150 |
| South | CityC | 200 |
| South | CityD | 250 |
Сумування за допомогою GROUP BY
Ми хочемо дізнатися загальний обсяг продажів по кожному регіону:
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
Результат буде такий:
| region | total_sales |
|---|---|
| North | 250 |
| South | 450 |
Що сталося: рядки згрупувалися по region, і кожна група була "стиснута" в один рядок із сумою продажів.
Сумування за допомогою PARTITION BY
Тепер зробимо те ж саме з window-функцією:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales_data;
Результат:
| region | city | sales | total_sales_by_region |
|---|---|---|---|
| North | CityA | 100 | 250 |
| North | CityB | 150 | 250 |
| South | CityC | 200 | 450 |
| South | CityD | 250 | 450 |
Що сталося: PARTITION BY не "стиснув" рядки. Замість цього він зробив розрахунок суми всередині певних вікон (кожен регіон — це окреме вікно).
Коли юзати GROUP BY, а коли — PARTITION BY?
GROUP BY: підходить для фінальних звітів
GROUP BY корисний, коли ти хочеш зменшити обсяг даних і отримати підсумкові результати на рівні груп. Наприклад:
- Сумарні продажі по місяцях.
- Підрахунок кількості замовлень по категоріях товарів.
Приклад:
SELECT category, COUNT(*) AS total_orders
FROM orders
GROUP BY category;
PARTITION BY: ідеальний для аналізу та деталізації
PARTITION BY підходить, коли треба зберегти всі рядки даних і додатково порахувати щось для кожного з них. Наприклад:
- Визначити долю продажів кожного товару в категорії.
- Нумерація рядків всередині кожної групи.
Приклад розрахунку долі продажів:
SELECT
category,
product,
sales,
ROUND(
(sales * 100.0) / SUM(sales) OVER (PARTITION BY category),
2
) AS sales_percentage
FROM sales_data;
Приклад: використання кількох window-функцій
Одна з переваг window-функцій — можливість юзати кілька обчислень одразу. Наприклад:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales,
RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS sales_rank
FROM sales_data;
Результат:
| region | city | sales | total_sales | sales_rank |
|---|---|---|---|---|
| North | CityB | 150 | 250 | 1 |
| North | CityA | 100 | 250 | 2 |
| South | CityD | 250 | 450 | 1 |
| South | CityC | 200 | 450 | 2 |
Переваги window-функцій над GROUP BY
Збереження оригінальних даних: GROUP BY "стискає" рядки, а window-функції дозволяють зберегти оригінальну структуру таблиці.
Кілька обчислень в одному запиті: Ти можеш юзати кілька window-функцій з різними параметрами PARTITION BY і ORDER BY, зберігаючи дані.
Гнучкість аналізу: Window-функції дозволяють налаштувати розрахунки під свої задачі: накопичувальні суми, ранжування, долеві розрахунки і ще купу всього.
Приклад гнучкості
Спробуємо об'єднати кілька функцій:
SELECT
region,
city,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_sales,
AVG(sales) OVER (PARTITION BY region) AS avg_sales,
RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS rank
FROM sales_data;
Результат:
| region | city | sales | total_sales | avg_sales | rank |
|---|---|---|---|---|---|
| North | CityB | 150 | 250 | 125.0 | 1 |
| North | CityA | 100 | 250 | 125.0 | 2 |
| South | CityD | 250 | 450 | 225.0 | 1 |
| South | CityC | 200 | 450 | 225.0 | 2 |
Обмеження та типові помилки
Одна з поширених помилок — спроба юзати PARTITION BY, коли треба "стиснути" дані. Наприклад, замість:
SELECT region, SUM(sales) AS total_sales
FROM sales_data
GROUP BY region;
Дехто намагається написати так:
SELECT
region,
SUM(sales) OVER (PARTITION BY region) AS total_sales
FROM sales_data;
Але це поверне всі рядки, не скоротивши обсяг даних (що не завжди те, що треба).
Тепер ти точно знаєш, коли юзати GROUP BY, а коли — window-функції. Це як вибір між молотком і викруткою: обидва інструменти працюють з цвяхами... але по-різному.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ