← Все вопросы

В чём разница между RANK() и DENSE_RANK() в SQL?

Задан 20 месяцев назад969 просмотров2 ответа
10

Использую оконные функции для ранжирования и наткнулся на две похожие: RANK() и DENSE_RANK(). На первый взгляд делают одно и то же. В чём между ними разница, особенно когда есть одинаковые значения (ничьи)? И чем они отличаются от ROW_NUMBER()? Пример на SQL приветствуется.

2 ответа

17
✓ Принятый ответ — помог автору

Все три функции — ROW_NUMBER(), RANK(), DENSE_RANK() — ранжируют строки внутри окна, но по-разному ведут себя при ничьих (одинаковых значениях в ORDER BY).

  • ROW_NUMBER() — всегда уникальные номера 1, 2, 3, ..., даже при равных значениях. Ничьи разрываются произвольно.
  • RANK() — при ничьей даёт одинаковый ранг, но затем пропускает номера (есть «дырки»).
  • DENSE_RANK() — при ничьей даёт одинаковый ранг и не пропускает следующие (без «дырок»).

Лучше всего видно на примере. Пусть есть очки игроков:

SELECT
    name,
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num,
    RANK()       OVER (ORDER BY score DESC) AS rnk,
    DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rnk
FROM players;

Результат для очков 100, 100, 90, 80:

name   score  row_num  rnk  dense_rnk
Anna    100      1       1       1
Boris   100      2       1       1
Clara    90      3       3       2
Dmitry   80      4       4       3

Обратите внимание на строку Clara (90 очков):

  • ROW_NUMBER = 3 — просто следующий по счёту.
  • RANK = 3 — потому что два игрока заняли первое место, третий получает ранг 3 (ранг 2 пропущен).
  • DENSE_RANK = 2 — «плотный» ранг, дырок нет, идёт сразу после первого места.

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

Когда что брать:

  • нужна спортивная таблица в стиле «1, 1, 3 место» → RANK();
  • нужны уровни/категории без пропусков (например, «топ-3 разных зарплат») → DENSE_RANK();
  • нужна строгая уникальная нумерация (дедупликация, пагинация) → ROW_NUMBER().
7

Короткая шпаргалка, которую я держу в голове на собеседованиях:

Значения:   100  100  90  80
ROW_NUMBER:   1    2   3   4   (всегда подряд, уникально)
RANK:         1    1   3   4   (ничья → пропуск)
DENSE_RANK:   1    1   2   3   (ничья → без пропуска)

Хороший практический кейс для DENSE_RANK() — найти N-ю по величине различную зарплату. Например, вторая по величине зарплата:

WITH r AS (
    SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS dr
    FROM employees
)
SELECT DISTINCT salary
FROM r
WHERE dr = 2;

Если бы здесь стоял RANK(), то при нескольких людях с максимальной зарплатой ранг 2 мог бы вообще не существовать — и запрос вернул бы пусто. Поэтому для «N-го уникального значения» правильнее именно DENSE_RANK().

Ваш ответ

Войдите, чтобы ответить на вопрос.
Поддержать проект