
Функция окна SQL
Расчленение оконной функции
Оконная функция — это мощная функция SQL, которая позволяет пользователям выполнять вычисления и анализ подмножества строк в запросе. Оконная функция выполняет вычисления с данными без агрегирования текущих строк.
Оконные функции вычисляют результат на основе скользящей оконной рамы. Рамка окна — это набор строк, связанных с текущей строкой. Связь заключается в том, что все строки имеют одно и то же значение для всех терминов предложения PARTITION BY в определении окна. Рамка окна оценивается отдельно в каждом разделе. Предложение Partition By разделяет строки на несколько групп.


Синтаксис
SELECT <column list>,
<window_function> OVER (
PARTITION BY <>
ORDER BY <>
<window_frame>
) window_alias
FROM <table_name>
OR
SELECT <column list> OVER <window_definition_name>
FROM <table_name>
WINDOW window_definition_name AS (
PARTITION BY <>
ORDER BY <>
<window_frame>
)
Пример
SELECT city, month,
SUM(amount) OVER (
PARTITION BY city
ORDER BY month
RANGE UNBOUNDED PRECEDING
) as total_sale
FROM Sales;
OR
SELECT city, month, SUM(amount) OVER window_definition_name
FROM Sales
WINDOW window_definition_name AS (
PARTITION BY city
ORDER BY month
RANGE UNBOUNDED PRECEDING
)
Вышеупомянутая оконная функция понимается следующим образом:

Примечание
- Предложение PARTITION BY необязательно, если мы не указываем предложение PARTITION BY, функция обрабатывает все строки как один раздел
- Предложение FRAME (ROWS | RANGE) дополнительно определяет подмножество текущего раздела.
Положение оконной функции в общем логическом порядке операторов:

Итак, из вышеизложенного мы знаем, что оконная функция применяется непосредственно перед запуском части выбора. К этому времени сервер базы данных выполнил все шаги, необходимые для оценки запроса, включая объединение, фильтрацию, группировку и сортировку, и результирующий набор завершен и готов к возврату вызывающей стороне. Оконная функция применяется к этому набору результатов, пока он все еще хранится в памяти.
В отличие от агрегатной функции оконная функция не сворачивает строки.

В спецификации окна мы можем предоставить три разных предложения фрейма:
ROWS BETWEEN – строки на расстоянии N от текущих строк в разделе.
RANGE – строки на основе их значения по сравнению с текущей строкой.
>ГРУППЫ — подсчитывает все группы связанных строк в окне (в Postgres).
[БЕЗ ГРАНИЦ] ПРЕДЫДУЩАЯ | ТЕКУЩАЯ РЯДКА | [БЕЗ ГРАНИЦ] СЛЕДУЮЩИЕ
Мы можем использовать следующие оконные функции:
Агрегатная функция
СЧЁТ, МАКС., МИН., СУММ., СРЕДН.
Функции ранжирования
ROW_NUMBER, РАНГ, DENSE_RANK
Функции распределения
PERCENT_RANK, CUME_DIST, NTILE
Позиционные функции
ОПЕРЕЖЕНИЕ, ЗАПАЗДАНИЕ, NTH_VALUE, FIRST_VALUE, LAST_VALUE
Синтаксис функции опережения/запаздывания.
LEAD | LAG ( expression, offset, deafult_value) OVER
Эти две функции полезны при сравнении годовых и квартальных показателей.
Аналитические функции
NTILE
Примечание. Мы не можем использовать оконную функцию в предложении where для фильтрации данных.
Проблема
185. Три лучших зарплаты отдела
Таблица сотрудников
+--------------+---------+ | Column Name | Type | +--------------+---------+ | id | int | | name | varchar | | salary | int | | departmentId | int | +--------------+---------+
Таблица отдела
+-------------+---------+ | Column Name | Type | +-------------+---------+ | id | int | | name | varchar | +-------------+---------+
Руководители компании заинтересованы в том, чтобы узнать, кто зарабатывает больше всего денег в каждом из отделов компании. Высокооплачиваемый в отделе – это сотрудник, чья зарплата входит в три самых уникальных зарплаты в этом отделе. Напишите SQL-запрос, чтобы найти сотрудников с высокими доходами в каждом из отделов. Возвратите таблицу результатов в любом порядке.
Пример
Input: Employee table: +----+-------+--------+--------------+ | id | name | salary | departmentId | +----+-------+--------+--------------+ | 1 | Joe | 85000 | 1 | | 2 | Henry | 80000 | 2 | | 3 | Sam | 60000 | 2 | | 4 | Max | 90000 | 1 | | 5 | Janet | 69000 | 1 | | 6 | Randy | 85000 | 1 | | 7 | Will | 70000 | 1 | +----+-------+--------+--------------+ Department table: +----+-------+ | id | name | +----+-------+ | 1 | IT | | 2 | Sales | +----+-------+ Output: +------------+----------+--------+ | Department | Employee | Salary | +------------+----------+--------+ | IT | Max | 90000 | | IT | Joe | 85000 | | IT | Randy | 85000 | | IT | Will | 70000 | | Sales | Henry | 80000 | | Sales | Sam | 60000 | +------------+----------+--------+ Explanation: In the IT department: - Max earns the highest unique salary - Both Randy and Joe earn the second-highest unique salary - Will earns the third-highest unique salary In the Sales department: - Henry earns the highest salary - Sam earns the second-highest salary - There is no third-highest salary as there are only two employees
Решение:
Сначала мы присваиваем ранг всем окладам в отделе. Мы достигаем этого, используя оконную функцию, как показано ниже:
SELECT
departmentId, name, salary,
DENSE_RANK() OVER (PARTITION BY departmentId ORDER BY Salary desc) as r
FROM Employee
Вывод вышеуказанного запроса выглядит следующим образом:
+--------------+-------+--------+----+ | departmentId | name | salary | r | +--------------+-------+--------+----+ | 1 | Max | 90000 | 1 | | 1 | Joe | 85000 | 2 | | 1 | Randy | 60000 | 2 | | 1 | Will | 90000 | 3 | | 1 | Janet | 69000 | 4 | | 2 | Henry | 85000 | 1 | | 2 | Sam | 70000 | 2 | +--------------+-------+--------+----+
Получив этот результат, мы просто отфильтровываем строки с рангом ‹ 4 (1, 2, 3).
WITH ranked_salary_for_each_dept AS (
SELECT departmentId, name, salary, DENSE_RANK() OVER (PARTITION BY departmentId ORDER BY Salary desc) as r
FROM Employee
)
select d.Name as "Department", a.Name as "Employee", a.Salary as "Salary"
from ranked_salary_for_each_dept a
join Department d on a.departmentId = d.Id
where a.r < 4
Счастливого Окна!!