Коли ти юзаєш window functions, виникає питання: "Скільки рядків всередині вікна бере участь у розрахунку значення для поточного рядка?" Відповідь на це залежить від віконного фрейму.
Віконний фрейм — це діапазон рядків, який використовується для обчислення результату window function. Цей діапазон будується на основі поточного рядка, а також додаткових умов, заданих через ROWS або RANGE.
Простий приклад: рахуючи накопичувальну суму, ти можеш вказати:
- Враховувати тільки поточний рядок.
- Враховувати поточний рядок і всі рядки вище.
- Враховувати поточний рядок і фіксовану кількість рядків вище/нижче.
Саме ROWS і RANGE керують тим, які рядки потраплять у віконний фрейм.
Використання ROWS
ROWS визначає віконний фрейм на рівні фізичного розташування рядків. Це означає, що воно рахує рядки зверху вниз у їхньому порядку, незалежно від значень у цих рядках.
Синтаксис
window_function OVER (
ORDER BY стовпець
ROWS BETWEEN початок AND кінець
)
Ключові вирази:
CURRENT ROW— поточний рядок.число PRECEDING— певна кількість рядків вище поточного.число FOLLOWING— певна кількість рядків нижче поточного.UNBOUNDED PRECEDING— від початку вікна.UNBOUNDED FOLLOWING— до кінця вікна.
Приклад: накопичувальна сума для поточного рядка і 2 попередніх
SELECT
employee_id,
salary,
SUM(salary) OVER (
ORDER BY employee_id
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS rolling_sum
FROM employees;
Пояснення:
-
ROWS BETWEEN 2 PRECEDING AND CURRENT ROWозначає: візьми поточний рядок і два рядки вище нього. - Накопичувальна сума буде рахуватись тільки для цих трьох рядків.
Результат:
| employee_id | salary | rolling_sum |
|---|---|---|
| 1 | 5000 | 5000 |
| 2 | 7000 | 12000 |
| 3 | 6000 | 18000 |
| 4 | 4000 | 17000 |
Приклад: аналіз "ковзаючого вікна" з фіксованою кількістю рядків
Задача: порахувати середню зарплату для поточного рядка і двох наступних.
SELECT
employee_id,
salary,
AVG(salary) OVER (
ORDER BY employee_id
ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING
) AS rolling_avg
FROM employees;
Результат:
| employee_id | salary | rolling_avg |
|---|---|---|
| 1 | 5000 | 6000 |
| 2 | 7000 | 5666.67 |
| 3 | 6000 | 5000 |
| 4 | 4000 | 4000 |
Використання RANGE
RANGE будує віконний фрейм на основі значень, а не позиційного розташування рядків. Це означає, що рядки включаються у фрейм, якщо їх значення у стовпці ORDER BY потрапляють у вказаний діапазон.
Синтаксис
window_function OVER (
ORDER BY стовпець
RANGE BETWEEN початок AND кінець
)
Приклад: накопичувальна сума по діапазону значень
Задача: порахувати накопичувальну суму для рядків, де зарплата відрізняється від поточної не більше ніж на 2000.
SELECT
employee_id,
salary,
SUM(salary) OVER (
ORDER BY salary
RANGE BETWEEN 2000 PRECEDING AND 2000 FOLLOWING
) AS range_sum
FROM employees;
Пояснення:
RANGE BETWEEN 2000 PRECEDING AND 2000 FOLLOWINGозначає: візьми рядки, де значенняsalaryзнаходиться у діапазоні ±2000 від поточного рядка.
Результат:
| employee_id | salary | range_sum |
|---|---|---|
| 4 | 4000 | 10000 |
| 3 | 6000 | 17000 |
| 2 | 7000 | 17000 |
| 1 | 5000 | 17000 |
Порівняння ROWS і RANGE
ROWSпрацює з реальними рядками і їх кількістю. Він не залежить від значень.RANGEпрацює з логічним діапазоном значень, заданим для стовпця зORDER BY.
Для порівняння наведемо приклад. Припустимо, у нас є таблиця sales з даними:
| id | amount |
|---|---|
| 1 | 100 |
| 2 | 100 |
| 3 | 300 |
| 4 | 400 |
Порівняємо запити:
ROWS:
SELECT
id,
SUM(amount) OVER (
ORDER BY amount
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS sum_rows
FROM sales;
Результат:
| id | sum_rows |
|---|---|
| 1 | 100 |
| 2 | 200 |
| 3 | 500 |
| 4 | 900 |
Тут кожен рядок система додає до суми по мірі їх реальної появи.
RANGE:
SELECT
id,
SUM(amount) OVER (
ORDER BY amount
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS sum_range
FROM sales;
Результат:
| id | sum_range |
|---|---|
| 1 | 200 |
| 2 | 200 |
| 3 | 500 |
| 4 | 900 |
Тут рядки 1 і 2 об'єдналися, бо їх amount = 100. RANGE враховує повторювані значення у стовпці amount.
Приклади реальних задач
- Розрахунок приросту доходу
Задача: порахувати зміну доходу порівняно з попереднім рядком.
SELECT
month,
revenue,
revenue - LAG(revenue) OVER (
ORDER BY month
) AS revenue_change
FROM sales_data;
- Порівняння поточного рядка із середнім у групі
Задача: для кожного відділу порахувати різницю зарплати співробітника із середньою зарплатою по відділу.
SELECT
department_id,
employee_id,
salary,
salary - AVG(salary) OVER (
PARTITION BY department_id
) AS salary_diff
FROM employees;
Помилки при використанні ROWS і RANGE
Неправильно вказаний порядок рядків (ORDER BY): Якщо не вказати порядок сортування, PostgreSQL видасть помилку, бо не зможе визначити поточний рядок.
Змішування підходів ROWS і RANGE в одній задачі: Обирай підхід залежно від твоїх даних. ROWS підходить для задач із фіксованою кількістю рядків, а RANGE — для діапазонів значень.
Пропуск повторюваних значень у RANGE: Пам'ятай, що RANGE враховує всі повторювані значення, що може суттєво змінити результат.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ