Зарегистрируйтесь, чтобы продолжить обучение

ROW_NUMBER SQL: Оконные функции

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

Функция ROW_NUMBER() помогает задать нумерацию, которая будет отличаться от порядка сортировки строк результирующего набора. Допустим, вам нужно пронумеровать строки — то есть поставить номер строки. При этом нужно работать не со всеми записями в таблицах, а только в определенных разрезах или по определенной логике.

Использование ROW_NUMBER

Используем ROW_NUMBER() OVER (), чтобы пронумеровать продажи

  SELECT
      id,
      sale_date,
      quantity,
      price,
      ROW_NUMBER() OVER () AS sale_number
  FROM sales;
id sale_date quantity price sale_number
1 2023-01-01T00:00:00.000Z 1 4 1
2 2023-01-01T00:00:00.000Z 2 2 2
3 2023-01-01T00:00:00.000Z 1 1 3
4 2023-01-01T00:00:00.000Z 1 2 4
5 2023-01-02T00:00:00.000Z 1 3 5
6 2023-01-02T00:00:00.000Z 1 3 6
7 2023-01-03T00:00:00.000Z 1 1 7
8 2023-01-03T00:00:00.000Z 1 3 8
9 2023-01-03T00:00:00.000Z 1 1 9
10 2023-01-03T00:00:00.000Z 1 1 10
...

View on DB Fiddle

В столбце sale_number каждая строка имеет уникальный номер продажи. Нумерация начинается с 1 и увеличивается на 1 для каждой следующей строки.

Расширим функцию и добавим партици. Следующий запрос присваивает уникальный номер каждой строке внутри каждой группы, определенной PARTITION BY.

SELECT
    id,
    sale_date,
    quantity,
    price,
    ROW_NUMBER() OVER (PARTITION BY sale_date) AS sale_number
FROM sales;
id sale_date quantity price sale_number
1 2023-01-01T00:00:00.000Z 1 4 1
2 2023-01-01T00:00:00.000Z 2 2 2
3 2023-01-01T00:00:00.000Z 1 1 3
4 2023-01-01T00:00:00.000Z 1 2 4
5 2023-01-02T00:00:00.000Z 1 3 1
6 2023-01-02T00:00:00.000Z 1 3 2
7 2023-01-03T00:00:00.000Z 1 1 1
8 2023-01-03T00:00:00.000Z 1 3 2
9 2023-01-03T00:00:00.000Z 1 1 3
10 2023-01-03T00:00:00.000Z 1 1 4
11 2023-01-03T00:00:00.000Z 1 2 5
...

View on DB Fiddle

Разберем подробно запрос. PARTITION BY sale_date - создает группы, окна, по уникальной дате продаже. ROW_NUMBER() OVER (PARTITION BY sale_date) AS sale_number - применяет функцию ROW_NUMBER() к каждой строке данных внутри группы. Таким образом, каждая строка внутри каждой группы будет иметь уникальный номер, который будет отображаться в столбце sale_number.

Внутри оператора OVER () можем использовать сортировку, чтобы указать порядок для нумерации строк.

SELECT
    id,
    sale_date,
    quantity,
    price,
    row_number() OVER (PARTITION BY sale_date ORDER BY quantity) AS sale_number
FROM sales;
id sale_date quantity price sale_number
4 2023-01-01T00:00:00.000Z 1 2 1
1 2023-01-01T00:00:00.000Z 1 4 2
3 2023-01-01T00:00:00.000Z 1 1 3
2 2023-01-01T00:00:00.000Z 2 2 4
5 2023-01-02T00:00:00.000Z 1 3 1
6 2023-01-02T00:00:00.000Z 1 3 2
7 2023-01-03T00:00:00.000Z 1 1 1
8 2023-01-03T00:00:00.000Z 1 3 2
9 2023-01-03T00:00:00.000Z 1 1 3
10 2023-01-03T00:00:00.000Z 1 1 4
11 2023-01-03T00:00:00.000Z 1 2 5
...

View on DB Fiddle

Теперь порядок строк изменился. На первом месте запись с id = 4, но результат может меняться, так как в колонке quantity у нескольких записей одинаковые значения. При сортировке лучше использовать поля или значения, которые помогут однозначно отсортировать значения, например дата продажи с точностью до секунды.

Для сортировки можно также использовать вычисляемые значения

SELECT
    id,
    sale_date,
    quantity,
    price,
    (quantity * price) AS amount,
    row_number()
        OVER (PARTITION BY sale_date ORDER BY quantity * price DESC)
    AS sale_number
FROM sales
WHERE sale_date = '01.01.2023' ORDER BY sale_number ASC;
id sale_date quantity price amount sale_number
1 2023-01-01T00:00:00.000Z 1 4 4 1
2 2023-01-01T00:00:00.000Z 2 2 4 2
4 2023-01-01T00:00:00.000Z 1 2 2 3
3 2023-01-01T00:00:00.000Z 1 1 1 4

View on DB Fiddle

Удаление дубликатов с помощью ROW_NUMBER

С помощью ROW_NUMBER() можно избавиться от дубликатов в запросе. Для этого в PARTITION BY указывают столбцы, по котором необходимо найти уникальность, а также используют подзапрос для фильтрации:

WITH tab AS (
    SELECT
        sale_date,
        row_number() OVER (PARTITION BY sale_date ORDER BY id) AS rn
    FROM sales
)

SELECT sale_date FROM tab WHERE rn = 1;
sale_date
2023-01-01T00:00:00.000Z
2023-01-02T00:00:00.000Z
2023-01-03T00:00:00.000Z
2023-01-04T00:00:00.000Z
2023-01-05T00:00:00.000Z
2023-01-06T00:00:00.000Z
2023-01-07T00:00:00.000Z
2023-01-08T00:00:00.000Z
2023-01-09T00:00:00.000Z
2023-01-10T00:00:00.000Z
2023-01-11T00:00:00.000Z
2023-01-12T00:00:00.000Z
2023-01-13T00:00:00.000Z ...

View on DB Fiddle

Здесь запрос можно разделить на две логические части. Сперва мы создали группы по дате и пронумеровали строки внутри каждой группы. Затем офильтровали строки, оставив лишь те, что в группе под номером 1. Таким образом, мы избавились от дубликатов.

Выводы

  • ROW_NUMBER() - это аналитическая функция в SQL, которая присваивает каждой строке результата запроса уникальный числовой идентификатор. Синтаксис ROW_NUMBER() выглядит примерно так:
ROW_NUMBER() OVER (PARTITION BY column1, column2 ORDER BY column3)
  • ROW_NUMBER() начинает с 1 для первой строки каждой группы, увеличиваясь на 1 для каждой следующей строки.
  • ROW_NUMBER() может быть использован для нумерации строк в результирующем наборе данных, а также для фильтрации и других операций.

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff