Ответ
Оконные функции (Window Functions) в SQL позволяют выполнять вычисления над набором строк, связанных с текущей строкой, без сворачивания результатов в одну строку, в отличие от агрегатных функций с GROUP BY. Они сохраняют все исходные строки результата.
Ключевые компоненты:
OVER(): Обязательная конструкция, определяющая "окно" — набор строк для вычисления.PARTITION BY: Разбивает данные на группы (партиции), внутри которых работает функция. АналогичноGROUP BY, но без агрегации.ORDER BY: Задает порядок строк внутри окна или партиции, что критично для функций ранжирования и накопительных итогов.ROWS/RANGE BETWEEN: Определяет фрейм — точный поднабор строк внутри окна для расчета (например,ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING).
Основные типы оконных функций:
- Ранжирующие:
ROW_NUMBER(),RANK(),DENSE_RANK(),NTILE(). - Агрегатные:
SUM(),AVG(),COUNT(),MIN(),MAX()(используемые сOVER). - Функции смещения:
LAG()(значение из предыдущей строки),LEAD()(значение из следующей строки). - Функции доступа к значениям:
FIRST_VALUE(),LAST_VALUE(),NTH_VALUE().
Практический пример — расчет накопительного итога и ранга:
SELECT
employee_id,
department_id,
salary,
-- Накопительная сумма зарплат по отделам
SUM(salary) OVER (
PARTITION BY department_id
ORDER BY hire_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total,
-- Ранг зарплаты внутри отдела
RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS salary_rank_in_dept
FROM employees;
Это мощный инструмент для сложных аналитических запросов без необходимости использования коррелированных подзапросов.
Ответ 18+ 🔞
Давай разберём эту штуку, а то народ пугается, как будто это хуй с горы какой-то. Оконные функции в SQL — это, блядь, просто пиздец как удобно, когда тебе надо посчитать что-то по группе, но при этом все строки на экране оставить, а не сворачивать в одну кучу, как делает обычный GROUP BY.
Представь, у тебя таблица с зарплатами. Хочешь узнать, кто в отделе самый жирный, но при этом видеть всех подряд, а не только топ-1? Вот тут они и выручают, эти оконные функции. Ёпта, это как смотреть на всю толпу и сразу видеть, у кого самая дорогая тачка, но при этом всех остальных тоже видно.
Из чего это говно собрано:
OVER(): Это, сука, основа основ. Без этого — нихуя не работает. Внутри него ты и определяешь, над каким именно «окошком» данных будет колдовать функция.PARTITION BY: Делит твои данные на группы. Скажем, по отделам. Функция будет работать внутри каждой группы отдельно. Это какGROUP BY, только строки не схлопываются — красота!ORDER BY: Задает порядок. Без него некоторые функции будут работать как попало, а другие — вообще нихуя не делать. Особенно важно для подсчета всяких накопительных сумм.ROWS/RANGE BETWEEN: Это уже тонкая настройка, хитрая жопа. Тут ты говоришь: «Считай не по всем строкам партиции, а, например, только по предыдущей и следующей от текущей». Мощно, но сначала голова болеть будет.
Какие бывают эти функции, ёпта:
- Ранжирующие:
ROW_NUMBER(),RANK(),DENSE_RANK(). Выдают места: первый, второй, третий... Только вотRANK()может дать одинаковые места, а потом прыгнуть (1, 2, 2, 4), аDENSE_RANK()— нет (1, 2, 2, 3).ROW_NUMBER()всегда уникальный, даже если значения одинаковые. Волнение ебать, когда выбираешь не ту. - Агрегатные: Те же самые
SUM(),AVG(), но с прицепомOVER(). Они не схлопывают строки, а считают агрегат для каждой строки в её окне. Овердохуища возможностей открывается. - Функции смещения:
LAG()иLEAD(). Ни хуя себе, полезные штуки.LAGсмотрит на предыдущую строку (в заданном порядке),LEAD— на следующую. Идеально для сравнения «сегодня vs вчера» в одном запросе. - Функции доступа:
FIRST_VALUE(),LAST_VALUE(). Берут первое или последнее значение в окне. СLAST_VALUE()надо аккуратнее, по умолчанию её окно может вести себя неочевидно.
Смотри, как это выглядит в деле. Вот тебе реальный пример, чтобы не быть голословным:
SELECT
employee_id,
department_id,
salary,
-- Накопительная сумма зарплат по отделам
SUM(salary) OVER (
PARTITION BY department_id
ORDER BY hire_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS running_total,
-- Ранг зарплаты внутри отдела
RANK() OVER (
PARTITION BY department_id
ORDER BY salary DESC
) AS salary_rank_in_dept
FROM employees;
Видишь? Мы взяли всех сотрудников. Для каждого мы посчитали, сколько всего бабла уже выплатили его отделу с самого начала (по дате приёма) и до него. И заодно прилепили рейтинг его зарплаты внутри отдела. И всё это — в одном запросе, без этих пиздопроебищных коррелированных подзапросов, которые всю производительность в трубу выдувают.
Короче, инструмент — ядрёна вошь. Сначала кажется сложным, но как врубишься — обратной дороги нет. Просто э, бошка, думай, когда окна настраиваешь.