Уяви собі, що ти працюєш офіціантом (або баристою, якщо ти фанат кави) у великому ресторані. Щодня ти підбиваєш підсумок по зароблених чайових. Але є нюанс: ресторан поділений на зони, і тебе цікавить, скільки чайових зароблено в кожній зоні окремо. PARTITION BY — це те, що SQL юзає для "розділення ресторану на зони".
Більш формально, PARTITION BY використовується у window-функціях для розділення всіх рядків таблиці на окремі групи (або "розділи"). Усередині кожної групи window-функція виконується заново. Це як ніби ти застосовуєш функцію окремо в кожному "розділі".
Приклад: як це працює
Допустимо, у нас є таблиця sales з даними про продажі:
| region | salesperson | amount |
|---|---|---|
| North | Alice | 100 |
| North | Bob | 200 |
| South | Alice | 150 |
| South | Charlie | 250 |
Якщо ми хочемо порахувати, скільки грошей заробив кожен продавець, але окремо для кожного регіону, PARTITION BY — це саме те, що треба.
Синтаксис PARTITION BY
Синтаксис дуже простий:
window_function() OVER (PARTITION BY стовпець_або_стовпці)
window_function()— це, наприклад,SUM(),AVG(),ROW_NUMBER()і так далі.PARTITION BY стовпець— вказує, по якому стовпцю треба розділити рядки.OVER()— це оператор, який каже SQL: "Зроби щось в рамках вказаного window".
Приклад: сума по групах
Давай порахуємо суму продажів для кожного регіону:
SELECT
region,
salesperson,
amount,
SUM(amount) OVER (PARTITION BY region) AS total_sales_by_region
FROM sales;
Результат буде такий:
| region | salesperson | amount | total_sales_by_region |
|---|---|---|---|
| North | Alice | 100 | 300 |
| North | Bob | 200 | 300 |
| South | Alice | 150 | 400 |
| South | Charlie | 250 | 400 |
Що відбувається? SQL ділить рядки на групи по значенню стовпця region (North і South), потім застосовує функцію SUM() окремо для кожної групи. Як результат, рядки всередині групи "North" отримують одне й те саме значення суми, а рядки всередині групи "South" — інше.
Приклади використання PARTITION BY
Давай подивимось, як PARTITION BY може бути корисним у реальних задачах.
Приклад 1: Ранжування всередині групи
Припустимо, ми хочемо ранжувати продавців всередині кожного регіону по кількості продажів. Для цього можна використати комбінацію PARTITION BY і функції RANK():
SELECT
region,
salesperson,
amount,
RANK() OVER (PARTITION BY region ORDER BY amount DESC) AS rank_in_region
FROM sales;
Результат:
| region | salesperson | amount | rank_in_region |
|---|---|---|---|
| North | Bob | 200 | 1 |
| North | Alice | 100 | 2 |
| South | Charlie | 250 | 1 |
| South | Alice | 150 | 2 |
Функція RANK() присвоює ранг всередині кожної групи region, починаючи з 1. Зверни увагу, що для кожної групи ранги стартують з одиниці.
Приклад 2: Порівняння кожного значення із середнім у групі
Допустимо, ми хочемо подивитися, скільки кожен продавець заробив відносно середнього по своєму регіону. Використаємо AVG():
SELECT
region,
salesperson,
amount,
AVG(amount) OVER (PARTITION BY region) AS avg_sales_by_region,
amount - AVG(amount) OVER (PARTITION BY region) AS diff_from_avg
FROM sales;
Результат:
| region | salesperson | amount | avg_sales_by_region | diff_from_avg |
|---|---|---|---|---|
| North | Alice | 100 | 150 | -50 |
| North | Bob | 200 | 150 | 50 |
| South | Alice | 150 | 200 | -50 |
| South | Charlie | 250 | 200 | 50 |
Спочатку SQL ділить рядки на групи по region. Потім рахує середнє AVG(amount) для кожної групи. Нарешті, для кожного рядка рахує різницю між його значенням і середнім.
Приклад 3: Нумерація рядків всередині групи
Скажімо, ти хочеш пронумерувати всі транзакції всередині кожної групи регіону. Використаємо ROW_NUMBER():
SELECT
region,
salesperson,
amount,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount DESC) AS row_number
FROM sales;
Результат:
| region | salesperson | amount | row_number |
|---|---|---|---|
| North | Bob | 200 | 1 |
| North | Alice | 100 | 2 |
| South | Charlie | 250 | 1 |
| South | Alice | 150 | 2 |
Порівняння з GROUP BY
Часто виникає плутанина між PARTITION BY і GROUP BY. Давай порівняємо їх:
GROUP BY
GROUP BY змінює структуру вихідного результату — він перетворює рядки таблиці в агрегати. Наприклад:
SELECT
region,
SUM(amount) AS total_sales
FROM sales
GROUP BY region;
Результат:
| region | total_sales |
|---|---|
| North | 300 |
| South | 400 |
Тут ми втрачаємо інфу про продавців, бо дані агрегуються.
PARTITION BY
PARTITION BY, навпаки, не змінює структуру. Ми все ще бачимо кожен рядок, але маємо додаткові значення, пораховані по групах. Тобто, PARTITION BY дозволяє агрегувати без втрати деталей.
Часті помилки при використанні PARTITION BY
Помилка 1: Забули PARTITION BY
Іноді ти хочеш групувати дані, але забуваєш використати PARTITION BY. Наприклад:
SELECT
region,
salesperson,
amount,
SUM(amount) OVER () AS total_sales
FROM sales;
Результат:
| region | salesperson | amount | total_sales |
|---|---|---|---|
| North | Alice | 100 | 700 |
| North | Bob | 200 | 700 |
| South | Alice | 150 | 700 |
| South | Charlie | 250 | 700 |
Тут SUM(amount) порахована для всієї таблиці, а не окремо для кожного регіону. Якщо хочеш враховувати регіони, не забудь вказати PARTITION BY region.
Помилка 2: Неправильний порядок в ORDER BY
Порядок рядків у window важливий для функцій, таких як RANK() чи ROW_NUMBER(). Будь уважний, коли юзаєш ORDER BY всередині OVER().
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ