Obsługa remisów w Top-N
Kiedy używać RANK lub DENSE_RANK, aby uwzględnić wszystkie wiersze z równymi wartościami
Obsługa remisów w Top-N to bezpłatna lekcja Coding Interview Prep na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Coding Interview Prep, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Coding Interview Prep zawiera 4 lekcji w sumie.
Dodatkowe pytanie o remisy
Gdy opanuje już Pan/Pani wybieranie pierwszych N elementów za pomocą ROW_NUMBER, osoba przeprowadzająca rozmowę może dopytać: "Co zrobić, jeśli dwóch pracowników ma dokładnie takie samo wynagrodzenie na granicy wybranego zakresu? Czy należy uwzględnić obie osoby?"
To odróżnia kandydatów, którzy zapamiętali jedno zapytanie, od tych, którzy rozumieją znaczenie poszczególnych rodzajów rankingu. Odpowiedź zależy od wyboru między ROW_NUMBER, RANK i DENSE_RANK. W tej lekcji każda funkcja zostanie przyporządkowana do odpowiedniej intencji biznesowej.
ROW_NUMBER ukrywa remisy
ROW_NUMBER przypisuje każdemu wierszowi ściśle unikatową liczbę całkowitą. Nawet gdy dwa wiersze mają taką samą wartość w kolumnie sortowania, jeden otrzyma 2, a drugi 3, w pewnej przypadkowej kolejności.
W konsekwencji filtr WHERE rn <= 3 zwraca dokładnie 3 wiersze i może po cichu pominąć remisujący wiersz, który również zasługiwał na uwzględnienie. Jest to właściwe, gdy rzeczywiście potrzebna jest stała liczba wierszy, ale nie wtedy, gdy wszystkie remisujące wiersze powinny się kwalifikować.
-- Salaries: 100, 90, 90, 80
-- ROW_NUMBER -> 1, 2, 3, 4 (the two 90s get 2 and 3 arbitrarily)
ROW_NUMBER() OVER (ORDER BY salary DESC)RANK pozostawia luki
RANK przypisuje remisującym wierszom to samo miejsce, a następnie pomija kolejne wartości, aby uwzględnić remis. Dwa wiersze remisujące na miejscu 2 otrzymują wartość 2, a następny wiersz otrzymuje miejsce 4, a nie 3.
Należy użyć RANK, gdy chodzi o „pierwsze 3 miejsca”, a jedno miejsce może przypadać kilku osobom — tak jak w rzeczywistych klasyfikacjach sportowych, w których dwóch srebrnych medalistów oznacza brak brązowego medalisty.
-- Salaries: 100, 90, 90, 80
-- RANK -> 1, 2, 2, 4 (rank 3 is skipped)
RANK() OVER (ORDER BY salary DESC)DENSE_RANK nie pozostawia luk
DENSE_RANK również przypisuje remisującym wierszom to samo miejsce, ale nie pomija kolejnego miejsca. Dwa wiersze remisujące na miejscu 2 otrzymują wartość 2, a następna różna wartość otrzymuje 3.
Należy użyć DENSE_RANK, gdy pytanie dotyczy różnych wartości: „3 najwyższych różnych poziomów wynagrodzeń” lub „3 najwyższych przedziałów cenowych”. Funkcja zlicza unikatowe wartości, a nie wiersze.
-- Salaries: 100, 90, 90, 80
-- DENSE_RANK -> 1, 2, 2, 3 (no gap)
DENSE_RANK() OVER (ORDER BY salary DESC)Porównanie obok siebie
Dla listy wynagrodzeń 100, 90, 90, 80 trzy funkcje zwracają:
- ROW_NUMBER: 1, 2, 3, 4
- RANK: 1, 2, 2, 4
- DENSE_RANK: 1, 2, 2, 3
Warto zapamiętać tę tabelę. Osoba przeprowadzająca rozmowę może po prostu poprosić o jej uzupełnienie dla podanej listy, a poprawne rozpoznanie zachowania luk jest tutaj najważniejsze.
SELECT salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS rn,
RANK() OVER (ORDER BY salary DESC) AS rnk,
DENSE_RANK() OVER (ORDER BY salary DESC) AS drnk
FROM employees;Uwzględnianie wszystkich remisujących wierszy za pomocą RANK
Aby odpowiedzieć na pytanie „pierwsze 3 miejsca, ale uwzględnij wszystkie osoby remisujące na granicy”, należy filtrować za pomocą RANK() <= 3. Jeśli dwie osoby zajmują miejsce 3, pojawią się obie, więc wynik może zawierać więcej niż 3 wiersze.
Taka jest intencja pytań sformułowanych jako „pierwsze 3 miejsca”, a nie „dowolne 3 wiersze”. Należy wyraźnie zaznaczyć, że przy remisach liczba wierszy może przekroczyć N.
WITH ranked AS (
SELECT name, department, salary,
RANK() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rnk
FROM employees
)
SELECT name, department, salary, rnk
FROM ranked
WHERE rnk <= 3
ORDER BY department, rnk;Trzy najwyższe różne poziomy za pomocą DENSE_RANK
Gdy wymagane są „3 najwyższe różne poziomy wynagrodzeń” i należy uwzględnić każdego pracownika na tych trzech poziomach, trzeba użyć DENSE_RANK() <= 3.
Takie zapytanie może zwrócić wiele wierszy: wszystkich pracowników z trzech najwyższych poziomów wynagrodzeń, niezależnie od tego, jak wiele osób znajduje się na każdym z nich. Kluczowa obserwacja jest taka, że DENSE_RANK zlicza różne wartości, więc filtr wybiera poziomy wartości, a nie stałą liczbę wierszy.
WITH ranked AS (
SELECT name, salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS drnk
FROM employees
)
SELECT name, salary, drnk
FROM ranked
WHERE drnk <= 3
ORDER BY drnk;Jak wybrać właściwą funkcję
Należy dopasować sformułowanie do funkcji:
- "Dokładnie N wierszy" lub stronicowanie →
ROW_NUMBER(należy dodać klucz rozstrzygający remis). - "Pierwsze N miejsc, przy czym remisujące osoby zajmują to samo miejsce" →
RANK. - "N najwyższych różnych wartości / poziomów / przedziałów" →
DENSE_RANK.
Gdy specyfikacja jest niejednoznaczna, należy zapytać: "Jeśli na granicy zakresu wystąpi remis, czy należy uwzględnić wszystkie remisujące wiersze, czy ograniczyć wynik do N?" Samo zadanie tego pytania doprecyzowującego może przynieść dodatkowe punkty.
Klucze rozstrzygające remisy nadal mają znaczenie
Nawet w przypadku RANK lub DENSE_RANK dodanie drugiego klucza ORDER BY kontroluje kolejność wyświetlania remisujących wierszy i zapewnia stabilność wyniku. Nie zmienia ono tego, które wiersze się kwalifikują, ale sprawia, że wyniki są powtarzalne.
W przypadku ROW_NUMBER klucz rozstrzygający remis jest niezbędny, ponieważ decyduje o tym, który remisujący wiersz w ogóle przetrwa filtr rn.
RANK() OVER (
PARTITION BY department
ORDER BY salary DESC, name ASC
) AS rnkPrzykład: najlepsi zdobywcy punktów
Tabela game_scores zawiera kolumny player i score. Osoba przeprowadzająca rozmowę chce „wszystkich graczy zajmujących 2 najwyższe miejsca pod względem wyniku”. Remis na pierwszym miejscu oznacza dwóch graczy na miejscu 1, a następny różny wynik otrzymuje miejsce 2.
„Miejsca” bez pomijania kolejnych miejsc po remisie wskazują na DENSE_RANK. Gdyby zamiast tego padło sformułowanie „2 najwyższe miejsca w klasyfikacji z pomijaniem miejsc”, należałoby użyć RANK. Należy zwracać uwagę na dokładne sformułowanie.
WITH r AS (
SELECT player, score,
DENSE_RANK() OVER (ORDER BY score DESC) AS pos
FROM game_scores
)
SELECT player, score, pos
FROM r
WHERE pos <= 2
ORDER BY pos, player;Typowe błędy, których należy unikać
Błędy, na które zwracają uwagę osoby przeprowadzające rozmowy kwalifikacyjne:
- Użycie
ROW_NUMBER, gdy pytanie wymaga uwzględnienia remisów, co prowadzi do cichego pominięcia kwalifikującego się wiersza. - Mylenie zachowania luk w funkcjach
RANKiDENSE_RANK. - Zapominanie, że filtry
RANK/DENSE_RANKmogą zwrócić więcej niż N wierszy, a następnie uznawanie takiego wyniku za błędny. - Pominięcie
PARTITION BY, gdy pierwsze N elementów ma być wybierane dla każdej grupy, co prowadzi do uszeregowania całej tabeli.
Szybkie sprawdzenie
Należy wybrać właściwą funkcję rankingową dla opisanego sposobu traktowania remisów.
Podsumowanie: obsługa remisów
Trzy funkcje, trzy sposoby obsługi remisów:
- ROW_NUMBER: zawsze unikalne numery, dokładnie N wierszy, a remisy są rozstrzygane arbitralnie.
- RANK: remisy mają wspólną pozycję, po czym pojawiają się luki.
- DENSE_RANK: remisy mają wspólną pozycję, nie ma luk, a zliczane są różne wartości.
Wybór zależy od sformułowania pytania. Gdy jest ono niejednoznaczne, należy zadać pytanie doprecyzowujące. Trzeba też pamiętać, że filtry RANK/DENSE_RANK mogą z założenia zwrócić więcej niż N wierszy.
Często zadawane pytania
Czy lekcja „Obsługa remisów w Top-N” jest bezpłatna?
Tak — pełny tekst „Obsługa remisów w Top-N” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Coding Interview Prep, przejdź na CoddyKit PRO. Kurs Coding Interview Prep zawiera 4 lekcji w sumie.
Co nauczysz się w „Obsługa remisów w Top-N”?
Kiedy używać RANK lub DENSE_RANK, aby uwzględnić wszystkie wiersze z równymi wartościami Ćwiczysz Coding Interview Prep z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Coding Interview Prep?
Nie wymagamy żadnego doświadczenia. Coding Interview Prep w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Obsługa remisów w Top-N”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Coding Interview Prep?
Tak. Każda lekcja Coding Interview Prep zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Wiersze Top-N dla każdej grupy za pomocą ROW_NUMBER
- Obsługa remisów w Top-N
- Bezpieczne usuwanie duplikatów wierszy
- Zachowywanie najnowszego wiersza dla każdego klucza