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

Другие функции SQL: Оконные функции

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

Функции RANK DENSE_RANK

Функция RANK() присваивает каждой строке результата запроса ранг в соответствии с заданным порядком. Если несколько строк имеют одинаковое значение, им будет присвоен одинаковый ранг, и следующий ранг будет увеличен на количество строк, занявших предыдущий ранг. Например, если у вас есть строки с рангами 1, 2, 2, то следующий ранг будет 4.

Функция DENSE_RANK() также присваивает ранг каждой строке, но в отличие от RANK(), она не пропускает ранги при наличии одинаковых значений. Это означает, что если несколько строк имеют одинаковое значение, им будет присвоен одинаковый ранг, но следующий ранг будет увеличен на 1, а не на количество строк, занявших предыдущий ранг. Например, если у вас есть строки с рангами 1, 2, 2, то следующий ранг будет 3.

Рассмотрим на примере. Посмотрим, как продались товары и какое место они занимают в рейтинге продаж:

WITH product_sums AS (
    SELECT
        p.name,
        SUM(s.quantity * s.price) AS amount
    FROM
        sales AS s
    INNER JOIN product AS p
        ON
            s.product_id = p.product_id
    GROUP BY
        p.name
    ORDER BY
        SUM(s.quantity * s.price) DESC
)

SELECT
    p.name,
    p.amount,
    ROW_NUMBER() OVER (ORDER BY p.amount) AS rn,
    RANK() OVER (ORDER BY p.amount) AS r,
    DENSE_RANK() OVER (ORDER BY p.amount) AS dr
FROM
    product_sums AS p;
name amount rn r dr
Toothpaste 6 1 1 1
Mint 6 2 1 1
Rice 6 3 1 1
Water 7 4 4 2
Tea 8 5 5 3
Porrige 8 6 5 3
Toast 8 7 5 3
Apple 9 8 8 4
IceCream 10 9 9 5
Potato 12 10 10 6
Tortilla 12 11 10 6
Salmon 12 12 10 6
Spagetti 12 13 10 6
Shampoo 12 14 10 6
Chocolate 12 15 10 6
Juice 14 16 16 7
Butter 15 17 17 8
Pork 15 18 17 8
Cheese 16 19 19 9
Tomato 16 20 19 9
Beef 18 21 21 10
Peer 24 22 22 11
Baguette 26 23 23 12
Cake 33 24 24 13

View on DB Fiddle

RANK() нумерует записи, но при этом учитывает повторяющиеся значения и присваивает им одинаковый ранг, после чего следующая запись получает ранг на единицу больше. Если есть несколько записей с одинаковым значением, они получат один и тот же ранг, а следующая запись получит следующий уникальный ранг. DENSE_RANK() пропускает ранги в случае повторяющихся значений. То есть он не оставляет промежутки между рангами, если есть одинаковые значения.

Таким образом, различие между RANK() и DENSE_RANK() в том, что RANK() может иметь пропуски в рангах при повторяющихся значениях, а DENSE_RANK() не имеет таких пропусков.

ROWS и RANGE

Инструкции ROWS и RANGE используются вместе с функциями оконного анализа, такими как RANK() и DENSE_RANK(), для определения диапазона строк, на которые должна распространяться аналитическая функция. ROWS определяет физические границы диапазона. Например, ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING означает текущую строку и две соседние строки, в то время как RANGE работает не со строками, а со значениями столбцов, отсортированных ORDER BY.

Например, вы можете использовать ROWS для вычисления суммы значений в столбце по диапазону строк, определенному ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING, или использовать RANGE для вычисления среднего значения в столбце по логическому диапазону значений.

Ключевые слова ROWS и RANGE:

  • UNBOUNDED PRECEDING указывает, что окно начинается с первой строки группы
  • UNBOUNDED FOLLOWING с помощью данной инструкции можно указать, что окно заканчивается на последней строке группы
  • CURRENT ROW инструкция указывает, что окно начинается или заканчивается на текущей строке
  • BETWEEN <граница окна> AND <граница окна> указывает нижнюю и верхнюю границу окна
  • <значение> PRECEDING определяет число строк перед текущей строкой (не допускается в предложении RANGE)
  • <значение> FOLLOWING определяет число строк после текущей строки (не допускается в предложении RANGE)

Рассмотрим пример по поиску среднего значения продажи между предыдущей, текущей и следующей покупкой

SELECT
    s.id,
    s.sale_date,
    (s.quantity * s.price) AS amount,
    SUM(s.quantity * s.price)
        OVER (ORDER BY s.id ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING),
    AVG(s.quantity * s.price)
        OVER (ORDER BY s.id ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING),
    COUNT(s.id) OVER ()
FROM
    sales AS s
ORDER BY
    s.sale_date;
id sale_date amount sum avg count
1 2023-01-01T00:00:00.000Z 4 8 4.0000000000000000 100
2 2023-01-01T00:00:00.000Z 4 9 3.0000000000000000 100
3 2023-01-01T00:00:00.000Z 1 7 2.3333333333333333 100
4 2023-01-01T00:00:00.000Z 2 6 2.0000000000000000 100
5 2023-01-02T00:00:00.000Z 3 8 2.6666666666666667 100

View on DB Fiddle

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

Выводы

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

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

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

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

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

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

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

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

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