Нумерация и ранжирование: ROW_NUMBER, RANK, DENSE_RANK
ROW_NUMBER, RANK и DENSE_RANK нумеруют строки — но по-разному обходятся с равными значениями.
Три функции ранжирования отличаются только поведением при ничьих:
ROW_NUMBERвсегда даёт уникальные номера,RANKповторяет ранг и оставляет «дыры»,DENSE_RANKповторяет ранг без «дыр».
Все три рядом
Дадим игрокам очки, где есть совпадения, и посмотрим на разницу в одной таблице.
CREATE TABLE score(player TEXT, points INTEGER);
INSERT INTO score VALUES ('Аня',100),('Борис',100),('Вика',90),('Глеб',90),('Дина',80);
SELECT player, points,
ROW_NUMBER() OVER (ORDER BY points DESC) AS rn,
RANK() OVER (ORDER BY points DESC) AS rnk,
DENSE_RANK() OVER (ORDER BY points DESC) AS dense
FROM score;
Вывод:
player points rn rnk dense ------ ------ -- --- ----- Аня 100 1 1 1 Борис 100 2 1 1 Вика 90 3 3 2 Глеб 90 4 3 2 Дина 80 5 5 3
Читаем колонки
- ROW_NUMBER (
rn): 1,2,3,4,5 — строго уникальные номера, даже у Ани и Бориса со 100 очками. Кто из них «первый» при равенстве — выбирается произвольно (стабилизировать можно, добавив второй ключ вORDER BY). - RANK (
rnk): 1,1,3,3,5 — равные значения получают одинаковый ранг, но следующий ранг «перепрыгивает» (после двух первых мест идёт 3-е, не 2-е). Это привычная спортивная таблица. - DENSE_RANK (
dense): 1,1,2,2,3 — равные тоже делят ранг, но «дыр» нет: после первого места сразу второе.
Что выбрать
| Нужно | Функция |
| уникальный порядковый номер (пагинация, «ровно по одному») | ROW_NUMBER |
| спортивный рейтинг с «дырами» после ничьих | RANK |
| плотные уровни/категории без пропусков | DENSE_RANK |
Подводный камень: ORDER BY обязателен
Для функций ранжирования ORDER BY внутри OVER обязателен — без него «номер строки» не имеет смысла. И помните: при ничьих ROW_NUMBER расставляет соседей недетерминированно, поэтому для воспроизводимости добавляйте уточняющий ключ, например ORDER BY points DESC, player.
Итог
ROW_NUMBER— уникальные 1,2,3,4,5; при равенстве порядок произволен.RANK— равным один ранг, дальше с «дырой» (1,1,3).DENSE_RANK— равным один ранг, без «дыр» (1,1,2).
Проверьте себя
1. Для очков 100,100,90 какие ранги даст RANK() по убыванию?
A1,1,2
B1,2,3
C1,1,3
D1,1,1
2. Для очков 100,100,90 какие ранги даст DENSE_RANK() по убыванию?
A1,1,2
B1,1,3
C1,2,3
D2,2,1
3. Когда выбрать ROW_NUMBER вместо RANK?
AКогда нужен спортивный рейтинг с пропусками
BКогда нужен строго уникальный порядковый номер каждой строке (например, для пагинации)
CКогда в данных нет ничьих — функции эквивалентны и разницы нет
DROW_NUMBER нельзя использовать с ORDER BY