Оконные функции MySQL

MySQLMySQLBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом практическом занятии (лабораторной работе) мы рассмотрим возможности оконных функций MySQL. Цель состоит в том, чтобы научиться выполнять вычисления для наборов строк таблицы, связанных с текущей строкой.

Мы начнем с присвоения уникального номера каждой строке с использованием функции ROW_NUMBER(), упорядочив сотрудников по зарплате. Затем мы вычислим накопительную сумму с помощью функции SUM OVER, используем PARTITION BY для групповых вычислений и, наконец, сравним строки с помощью функции LAG. В этом практическом занятии (лабораторной работе) представлены практические примеры, которые помогут вам понять и эффективно применить эти оконные функции.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL mysql(("MySQL")) -.-> mysql/BasicKeywordsandStatementsGroup(["Basic Keywords and Statements"]) mysql/BasicKeywordsandStatementsGroup -.-> mysql/create_table("Table Creation") mysql/BasicKeywordsandStatementsGroup -.-> mysql/select("Data Retrieval") mysql/BasicKeywordsandStatementsGroup -.-> mysql/insert("Data Insertion") subgraph Lab Skills mysql/create_table -.-> lab-550921{{"Оконные функции MySQL"}} mysql/select -.-> lab-550921{{"Оконные функции MySQL"}} mysql/insert -.-> lab-550921{{"Оконные функции MySQL"}} end

Назначение номеров строк с использованием ROW_NUMBER

На этом этапе мы рассмотрим, как использовать оконную функцию ROW_NUMBER() в MySQL для присвоения уникального последовательного целого числа каждой строке в наборе результатов. Это особенно полезно, когда вам нужно ранжировать данные или разбить их на страницы.

Прежде чем приступить к практическому примеру, давайте разберемся с базовым синтаксисом функции ROW_NUMBER():

ROW_NUMBER() OVER (ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...)
  • ROW_NUMBER(): Это сама оконная функция.
  • OVER(): Этот оператор определяет окно, то есть набор строк, на которых функция будет работать.
  • ORDER BY: Внутри оператора OVER() оператор ORDER BY определяет порядок, в котором номера строк будут присвоены. Вы можете указать одну или несколько столбцов для сортировки и выбрать порядок сортировки (по возрастанию (ASC) или по убыванию (DESC)) для каждого столбца.

Создадим таблицу с именем employees и вставим в нее некоторые данные. Откройте терминал и подключитесь к серверу MySQL с помощью командной строки mysql. Возможно, вам придется указать имя пользователя и пароль.

mysql -u root -p

После подключения выполните следующие SQL-инструкции для создания таблицы employees и вставки данных:

CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    employee_name VARCHAR(50),
    department VARCHAR(50),
    salary DECIMAL(10, 2)
);

INSERT INTO employees (employee_id, employee_name, department, salary) VALUES
(1, 'Alice', 'Sales', 60000.00),
(2, 'Bob', 'Marketing', 55000.00),
(3, 'Charlie', 'Sales', 65000.00),
(4, 'David', 'IT', 70000.00),
(5, 'Eve', 'Marketing', 62000.00);

Теперь используем функцию ROW_NUMBER() для присвоения номера каждой строке в таблице employees на основе зарплаты в порядке убывания. Выполните следующий запрос:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num
FROM
    employees;

Этот запрос вернет следующий результат:

+-------------+---------------+------------+----------+---------+
| employee_id | employee_name | department | salary   | row_num |
+-------------+---------------+------------+----------+---------+
|           4 | David         | IT         | 70000.00 |       1 |
|           3 | Charlie       | Sales      | 65000.00 |       2 |
|           5 | Eve           | Marketing  | 62000.00 |       3 |
|           1 | Alice         | Sales      | 60000.00 |       4 |
|           2 | Bob           | Marketing  | 55000.00 |       5 |
+-------------+---------------+------------+----------+---------+
5 rows in set (0.00 sec)

Как вы можете видеть, столбец row_num содержит номер строки, присвоенный каждому сотруднику на основе его зарплаты, причем сотрудник с самой высокой зарплатой получает номер 1.

Вы также можете использовать функцию ROW_NUMBER() с другими операторами ORDER BY. Например, чтобы присвоить номера строк на основе имени сотрудника в алфавитном порядке, вы можете использовать следующий запрос:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    ROW_NUMBER() OVER (ORDER BY employee_name ASC) AS row_num
FROM
    employees;

Это даст следующий результат:

+-------------+---------------+------------+----------+---------+
| employee_id | employee_name | department | salary   | row_num |
+-------------+---------------+------------+----------+---------+
|           1 | Alice         | Sales      | 60000.00 |       1 |
|           2 | Bob           | Marketing  | 55000.00 |       2 |
|           3 | Charlie       | Sales      | 65000.00 |       3 |
|           4 | David         | IT         | 70000.00 |       4 |
|           5 | Eve           | Marketing  | 62000.00 |       5 |
+-------------+---------------+------------+----------+---------+
5 rows in set (0.00 sec)

На этот раз столбец row_num отражает алфавитный порядок имен сотрудников.

Вычисление накопительной суммы с использованием SUM OVER

На этом этапе мы научимся вычислять накопительную сумму с помощью оконной функции SUM() OVER() в MySQL. Накопительная сумма, также известная как кумулятивная сумма, представляет собой сумму последовательности чисел, которая обновляется каждый раз, когда в последовательность добавляется новое число. Это полезно для отслеживания прогресса во времени, расчета накопленных продаж или анализа тенденций.

Базовый синтаксис функции SUM() OVER() выглядит следующим образом:

SUM(expression) OVER (ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...)
  • SUM(expression): Эта функция вычисляет сумму указанного выражения.
  • OVER(): Этот оператор определяет окно, то есть набор строк, на которых функция будет работать.
  • ORDER BY: Внутри оператора OVER() оператор ORDER BY определяет порядок, в котором сумма накапливается.

Мы продолжим использовать таблицу employees, созданную на предыдущем этапе. Если вы еще не создали ее, пожалуйста, обратитесь к предыдущему этапу для создания таблицы и вставки данных.

Теперь вычислим накопительную сумму зарплат всех сотрудников, упорядоченных по их employee_id. Выполните следующий запрос:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    SUM(salary) OVER (ORDER BY employee_id) AS running_total
FROM
    employees;

Этот запрос даст следующий результат:

+-------------+---------------+------------+----------+---------------+
| employee_id | employee_name | department | salary   | running_total |
+-------------+---------------+------------+----------+---------------+
|           1 | Alice         | Sales      | 60000.00 |      60000.00 |
|           2 | Bob           | Marketing  | 55000.00 |     115000.00 |
|           3 | Charlie       | Sales      | 65000.00 |     180000.00 |
|           4 | David         | IT         | 70000.00 |     250000.00 |
|           5 | Eve           | Marketing  | 62000.00 |     312000.00 |
+-------------+---------------+------------+----------+---------------+
5 rows in set (0.00 sec)

Столбец running_total показывает накопительную сумму зарплат по мере перемещения по строкам, упорядоченным по employee_id. Например, накопительная сумма для Боба (employee_id 2) представляет собой сумму зарплат Алисы и Боба (60000 + 55000 = 115000).

Вы также можете упорядочить накопительную сумму по другим столбцам, например, по зарплате. Вычислим накопительную сумму зарплат, упорядоченных по зарплате в порядке возрастания:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    SUM(salary) OVER (ORDER BY salary) AS running_total
FROM
    employees;

Результат будет следующим:

+-------------+---------------+------------+----------+---------------+
| employee_id | employee_name | department | salary   | running_total |
+-------------+---------------+------------+----------+---------------+
|           2 | Bob           | Marketing  | 55000.00 |      55000.00 |
|           1 | Alice         | Sales      | 60000.00 |     115000.00 |
|           5 | Eve           | Marketing  | 62000.00 |     177000.00 |
|           3 | Charlie       | Sales      | 65000.00 |     242000.00 |
|           4 | David         | IT         | 70000.00 |     312000.00 |
+-------------+---------------+------------+----------+---------------+
5 rows in set (0.00 sec)

Теперь накопительная сумма вычисляется на основе порядка возрастания зарплат.

Использование PARTITION BY для групповых вычислений

На этом этапе мы рассмотрим, как использовать оператор PARTITION BY в оконных функциях MySQL для выполнения вычислений на группах строк. PARTITION BY разбивает строки на разделы (партиции), и оконная функция применяется к каждой части независимо. Это полезно для вычисления агрегатов внутри групп, ранжирования элементов в категориях или сравнения значений в сегментах.

Базовый синтаксис использования PARTITION BY с оконной функцией выглядит так:

function(expression) OVER (PARTITION BY column1, column2, ... ORDER BY column3 [ASC|DESC], ...)
  • function(expression): Это оконная функция, которую вы хотите использовать (например, SUM(), AVG(), ROW_NUMBER()).
  • OVER(): Этот оператор определяет окно.
  • PARTITION BY: Этот оператор разбивает строки на разделы (партиции) на основе указанных столбцов.
  • ORDER BY: Внутри каждой части (партиции) оператор ORDER BY определяет порядок, в котором функция применяется.

Мы продолжим использовать таблицу employees, созданную на предыдущих этапах. Если вы еще не создали ее, пожалуйста, обратитесь к предыдущим этапам для создания таблицы и вставки данных.

Вычислим среднюю зарплату для каждого отдела с использованием PARTITION BY. Выполните следующий запрос:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    AVG(salary) OVER (PARTITION BY department) AS avg_salary_by_dept
FROM
    employees;

Этот запрос даст следующий результат:

+-------------+---------------+------------+----------+----------------------+
| employee_id | employee_name | department | salary   | avg_salary_by_dept |
+-------------+---------------+------------+----------+----------------------+
|           4 | David         | IT         | 70000.00 |           70000.0000 |
|           2 | Bob           | Marketing  | 55000.00 |           58500.0000 |
|           5 | Eve           | Marketing  | 62000.00 |           58500.0000 |
|           1 | Alice         | Sales      | 60000.00 |           62500.0000 |
|           3 | Charlie       | Sales      | 65000.00 |           62500.0000 |
+-------------+---------------+------------+----------+----------------------+
5 rows in set (0.00 sec)

Столбец avg_salary_by_dept показывает среднюю зарплату для отдела каждого сотрудника. Обратите внимание, что средняя зарплата одинакова для всех сотрудников в одном и том же отделе.

Теперь объединим PARTITION BY с ROW_NUMBER() для ранжирования сотрудников в каждом отделе по их зарплате. Выполните следующий запрос:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_by_salary
FROM
    employees;

Этот запрос даст следующий результат:

+-------------+---------------+------------+----------+----------------+
| employee_id | employee_name | department | salary   | rank_by_salary |
+-------------+---------------+------------+----------+----------------+
|           4 | David         | IT         | 70000.00 |              1 |
|           5 | Eve           | Marketing  | 62000.00 |              1 |
|           2 | Bob           | Marketing  | 55000.00 |              2 |
|           3 | Charlie       | Sales      | 65000.00 |              1 |
|           1 | Alice         | Sales      | 60000.00 |              2 |
+-------------+---------------+------------+----------+----------------+
5 rows in set (0.00 sec)

Столбец rank_by_salary показывает ранг каждого сотрудника в их отделе на основе их зарплаты, причем сотрудник с самой высокой зарплатой получает ранг 1. Ранжирование начинается заново для каждого отдела из-за оператора PARTITION BY department.

Сравнение строк с использованием LAG

На этом этапе мы научимся использовать оконную функцию LAG() в MySQL для доступа к данным из предыдущей строки в том же наборе результатов. Это полезно для сравнения значений между последовательными строками, вычисления разностей или выявления тенденций во времени.

Базовый синтаксис функции LAG() выглядит следующим образом:

LAG(expression, offset, default_value) OVER (PARTITION BY column1, column2, ... ORDER BY column3 [ASC|DESC], ...)
  • LAG(expression, offset, default_value): Эта функция возвращает значение expression из строки, которая находится на offset строк назад от текущей строки.
    • expression: Столбец или выражение, значение которого вы хотите получить из предыдущей строки.
    • offset: Количество строк, на которое нужно вернуться назад. Если параметр опущен, по умолчанию используется значение 1.
    • default_value: Значение, которое будет возвращено, если offset выходит за пределы начала раздела (партиции). Если параметр опущен, по умолчанию используется значение NULL.
  • OVER(): Этот оператор определяет окно.
  • PARTITION BY: Этот оператор разбивает строки на разделы (партиции) на основе указанных столбцов.
  • ORDER BY: Внутри каждой части (партиции) оператор ORDER BY определяет порядок, в котором строки обрабатываются.

Мы продолжим использовать таблицу employees, созданную на предыдущих этапах. Если вы еще не создали ее, пожалуйста, обратитесь к предыдущим этапам для создания таблицы и вставки данных.

Сравним зарплату каждого сотрудника с зарплатой предыдущего сотрудника, упорядоченных по employee_id. Выполните следующий запрос:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    LAG(salary, 1, 0) OVER (ORDER BY employee_id) AS previous_salary
FROM
    employees;

Этот запрос даст следующий результат:

+-------------+---------------+------------+----------+-----------------+
| employee_id | employee_name | department | salary   | previous_salary |
+-------------+---------------+------------+----------+-----------------+
|           1 | Alice         | Sales      | 60000.00 |          0.00 |
|           2 | Bob           | Marketing  | 55000.00 |     60000.00 |
|           3 | Charlie       | Sales      | 65000.00 |     55000.00 |
|           4 | David         | IT         | 70000.00 |     65000.00 |
|           5 | Eve           | Marketing  | 62000.00 |     70000.00 |
+-------------+---------------+------------+----------+-----------------+
5 rows in set (0.00 sec)

Столбец previous_salary показывает зарплату предыдущего сотрудника на основе employee_id. Для Алисы (employee_id 1) previous_salary равно 0, так как нет предыдущего сотрудника. Мы указали 0 в качестве default_value в функции LAG().

Теперь вычислим разницу между зарплатой каждого сотрудника и зарплатой предыдущего сотрудника. Выполните следующий запрос:

SELECT
    employee_id,
    employee_name,
    department,
    salary,
    salary - LAG(salary, 1, 0) OVER (ORDER BY employee_id) AS salary_difference
FROM
    employees;

Этот запрос даст следующий результат:

+-------------+---------------+------------+----------+-------------------+
| employee_id | employee_name | department | salary   | salary_difference |
+-------------+---------------+------------+----------+-------------------+
|           1 | Alice         | Sales      | 60000.00 |         60000.00 |
|           2 | Bob           | Marketing  | 55000.00 |         -5000.00 |
|           3 | Charlie       | Sales      | 65000.00 |         10000.00 |
|           4 | David         | IT         | 70000.00 |          5000.00 |
|           5 | Eve           | Marketing  | 62000.00 |         -8000.00 |
+-------------+---------------+------------+----------+-------------------+
5 rows in set (0.00 sec)

Столбец salary_difference показывает разницу между зарплатой каждого сотрудника и зарплатой предыдущего сотрудника.

Вы также можете использовать PARTITION BY с LAG(). Например, если у вас есть таблица с данными о продажах в разных регионах во времени, вы можете использовать PARTITION BY region для сравнения продаж в каждом регионе.

Резюме

В этом практическом занятии (лабораторной работе) мы изучили использование оконных функций MySQL, с особым упором на назначение номеров строк с помощью функции ROW_NUMBER(). Мы научились использовать оператор OVER() с ORDER BY для определения окна и порядка, в котором номера строк назначаются в наборе результатов. Это позволяет ранжировать данные и выполнять постраничный вывод.

Мы создали таблицу employees и заполнили ее тестовыми данными. Затем мы применили функцию ROW_NUMBER() для присвоения каждому сотруднику уникального последовательного целого числа на основе их зарплаты в порядке убывания, тем самым продемонстрировав практическое применение этой оконной функции.