Функция окна 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
Счастливого Окна!!