Что такое оконные функции в SQL и чем они отличаются от GROUP BY?

Ответ

Оконные функции (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. Главное — понять, что это не агрегация, а просто дополнительная колонка с умными цифрами для каждой строчки.