Najczęstsze pytanie na rozmowie SQL:

ROW_NUMBER vs RANK vs DENSE_RANK – czym się różnią i kiedy ich używać?

Jeśli byłeś na rozmowie rekrutacyjnej na stanowisko analityka, data engineera czy BI developera, bardzo możliwe, że padło to pytanie:

„Explain ROW_NUMBER vs RANK vs DENSE_RANK.”

To tzw. „Big Three” ranking functions w SQL.
I choć różnice wydają się subtelne, wybór złej funkcji może całkowicie zmienić logikę biznesową raportu.

ROW_NUMBER()

Każdy wiersz dostaje unikalny numer.
Brak remisów. Brak luk.

Przykład wyników:
1, 2, 3, 4

Jeśli dwie osoby mają taki sam wynik – i tak dostaną różne numery.


RANK()

W przypadku remisu wiersze dostają tę samą pozycję.
Kolejna pozycja zostaje „przeskoczona”.

Przykład:
1, 2, 2, 4
(„3” znika, bo były dwa drugie miejsca)


DENSE_RANK()

Remisy również mają tę samą pozycję.
Ale numeracja nie ma luk.

Przykład:
1, 2, 2, 3


Zobaczmy to na przykładzie

Załóżmy, że mamy ranking wyników:

Score?
95
90
90
85

W zależności od funkcji otrzymamy:

  • ROW_NUMBER: 1, 2, 3, 4
  • RANK: 1, 2, 2, 4
  • DENSE_RANK: 1, 2, 2, 3

Różnica wygląda niewinnie.
W praktyce może zmienić premię sprzedażową, dashboard KPI albo raport dla zarządu.


Wrong function = wrong business logic

To nie jest tylko pytanie rekrutacyjne.

Zła funkcja rankingowa może:

  • zdublować rekordy przy deduplikacji,
  • błędnie policzyć „Top 3”,
  • zmienić ranking najlepszych sprzedawców,
  • zaburzyć dashboard z leaderboardem.

W komentarzach do oryginalnego posta wiele osób zwracało uwagę, że użycie RANK() zamiast ROW_NUMBER() w deduplikacji potrafi zostawić dwa „najnowsze” rekordy.

Mała różnica w SQL. Duża różnica w biznesie.


ROW_NUMBER – gdy liczy się unikalna kolejność

Najczęstsze przypadki:

  • Paginacja (strona 1, 2, 3…)
  • Systemy kolejkowe (pozycja zgłoszenia)
  • Deduplikacja (zostaw najnowszy rekord)

Przykład deduplikacji:

SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC) AS rn
FROM users
) t
WHERE rn = 1;

Tu ROW_NUMBER() jest właściwym wyborem.
RANK() mógłby zostawić więcej niż jeden rekord.


RANK – gdy liczy się rzeczywista pozycja

Idealne do:

  • Rankingów sprzedaży
  • Wyników sportowych
  • Wyszukiwarek (ranking trafności)

Jeśli dwóch sprzedawców ma ten sam wynik – obaj są na 2. miejscu.
A kolejne miejsce to 4.

To często najbardziej „uczciwa” logika rankingowa.


DENSE_RANK – gdy nie chcesz luk

Sprawdza się w:

  • Kategoriach ocen (A, B, C)
  • Przydzielaniu nagród
  • „Top 5 produktów”

Tu nie chcemy, by po dwóch drugich miejscach pojawiło się czwarte.


Jak wybrać właściwą funkcję?

Zadaj sobie pytanie:

  1. Czy każdy wiersz musi mieć unikalny numer? → ROW_NUMBER
  2. Czy remis powinien powodować „dziurę” w numeracji? → RANK
  3. Czy remis bez luk jest bardziej logiczny? → DENSE_RANK

To nie kwestia składni.
To kwestia logiki biznesowej.


Dlaczego to najczęstsze pytanie rekrutacyjne?

Bo pokazuje, czy:

  • rozumiesz różnicę między danymi a ich interpretacją,
  • myślisz o konsekwencjach biznesowych,
  • potrafisz dobrać funkcję do celu,
  • rozumiesz window functions.

Rekruter nie sprawdza tylko SQL-a.
Sprawdza sposób myślenia.


Podsumowanie

FunkcjaRemisyLuki w numeracjiGłówne zastosowanie
ROW_NUMBERDeduplikacja, paginacja
RANKRankingi, leaderboard
DENSE_RANKKategorie, top N

Jedna linijka SQL może zmienić cały raport.

Dlatego warto rozumieć różnice — nie tylko na rozmowie kwalifikacyjnej, ale w realnych projektach.

Podobne wpisy