Ответ
Оконные функции (Window Functions) в SQL выполняют вычисления над набором строк (так называемым «окном»), которые связаны с текущей строкой. Главное их отличие от агрегатных функций с GROUP BY в том, что они не сворачивают (не коллапсируют) строки, а возвращают результат для каждой строки.
Ключевые отличия от GROUP BY:
- Сохранение строк: Оконная функция сохраняет исходное количество строк в результате,
GROUP BY— сжимает их до одной строки на группу. - Доступ к данным: Оконная функция имеет доступ к другим строкам в своем «окне», но результат выводит для текущей строки.
Синтаксис оконной функции определяется клаузой OVER():
FUNCTION_NAME() OVER ( [PARTITION BY ...] [ORDER BY ...] [FRAME_CLAUSE] )
PARTITION BY— делит строки на группы (партиции), для которых функция будет вычисляться независимо. АналогичноGROUP BY.ORDER BY— упорядочивает строки внутри партиции. Важно для функций ранжирования и функций, зависящих от порядка.FRAME_CLAUSE(ROWSилиRANGE) — определяет точные границы «окна» относительно текущей строки (например,ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING).
Пример:
Вывести зарплату каждого сотрудника, среднюю зарплату по его отделу и его ранг в этом отделе.
SELECT
employee_name,
department,
salary,
-- Средняя зарплата по отделу (окно - весь отдел)
AVG(salary) OVER(PARTITION BY department) as avg_dept_salary,
-- Ранг сотрудника по зарплате внутри отдела
RANK() OVER(PARTITION BY department ORDER BY salary DESC) as rank_in_dept
FROM employees;
Популярные категории функций:
- Ранжирующие:
ROW_NUMBER(),RANK(),DENSE_RANK(),NTILE(). - Смещения:
LEAD()(доступ к следующей строке),LAG()(доступ к предыдущей строке). - Агрегатные:
SUM(),AVG(),COUNT(),MAX(),MIN()как оконные функции.
Ответ 18+ 🔞
А, ну вот, опять про эти ваши оконные функции в SQL. Слушай, как будто без них жизнь не мила! Но раз уж начал, давай разберёмся, чтобы потом не сидеть с видом, будто тебе в рот нассали.
Представь себе, блядь, обычный GROUP BY. Он как тот занудный бухгалтер: взял все твои строки, скомкал их в кучки по отделам, посчитал среднюю зарплату и выдал тебе три строчки вместо тридцати. И сидишь ты, такой: "А где же Вася из отдела снабжения? Куда делся Петя?" А Вася и Петя, сука, сгинули в этой агрегации, их нихуя не видно!
А вот оконные функции — это другой коленкор. Они не сворачивают строки, нет! Они, хитрожопые, оставляют всех на виду. Каждому сотруднику выдают его зарплату, а рядом, как по волшебству, ещё и среднюю по отделу приписывают, и ранг его в отделе. И все строки на месте, ни одна не пропала! Это как если бы к каждому приставили персонального статиста, который шепчет на ухо: "Слушай, ты в своём отделе по зарплате третий, но средняя тут вот такая, держи в курсе".
Синтаксис у них, правда, выглядит так, будто его пьяный инженер придумал:
FUNCTION_NAME() OVER ( [PARTITION BY ...] [ORDER BY ...] [FRAME_CLAUSE] )
PARTITION BY— это типа "раздели всех по углам". Отдел снабжения — в один угол, маркетинг — в другой. Функция будет работать в каждом углу отдельно, не мешая друг другу.ORDER BY— а это "построиться по росту" внутри этого угла. Чтобы знать, кто за кем идёт. Без этого некоторые функции вообще нихуя не понимают.FRAME_CLAUSE— вот это уже высший пилотаж, ёпта. Это когда ты говоришь: "Считай не по всему отделу, а только по двум парням передо мной и одному сзади". Типа скользящее окно, блядь.
Пример, чтобы вообще всё встало на свои места:
Хочешь посмотреть на всех работяг, но при этом узнать, насколько они отстают от среднего по конторе и кто тут главный богач в отделе?
SELECT
employee_name,
department,
salary,
-- Средняя зарплата по отделу (окно - весь отдел)
AVG(salary) OVER(PARTITION BY department) as avg_dept_salary,
-- Ранг сотрудника по зарплате внутри отдела
RANK() OVER(PARTITION BY department ORDER BY salary DESC) as rank_in_dept
FROM employees;
И вуаля! Ты видишь, что Вася получает 100 тыщ, средняя в его отделе — 85, а ранг у него второй, потому что есть один упырь, который получает 120. И Петя тут же, со своими 70 тыщами и рангом четвёртым. Все на виду, все при своих интересах.
А функций этих — овердохуища видов:
- Ранжирующие:
ROW_NUMBER(),RANK(),DENSE_RANK(). Это те, которые расставляют всех по местам, как на пьедестале. ТолькоRANK()может дать одинаковые места и сделать пропуск, аDENSE_RANK()— нет, он жадина. - Смещения:
LEAD()иLAG(). Одна подглядывает, что в следующей строке записано, другая — что в предыдущей. Полезно, когда нужно сравнить "а что было вчера?". - Агрегатные: Да-да, те же самые
SUM(),AVG(), но только они теперь не сворачивают, а аккуратно считают для каждой строки в её окне. Красота, а не функция!
Вот и вся магия. Не так страшен чёрт, как его OVER() clause. Главное — понять, что это не агрегация, а просто дополнительная колонка с умными цифрами для каждой строчки.