Solving tips
- RANK leaves gaps after ties (1,1,3,...); DENSE_RANK does not (1,1,2,...) — pick based on whether skipped positions matter.
- Ties are defined purely by the ORDER BY expression of the window; two rows tie only if those values are equal.
- Neither function needs PARTITION BY unless you want the ranking to restart per group.
You have exam scores with several ties. Show, side by side, how RANK and DENSE_RANK number the tied rows differently.
Schema
CREATE TABLE exam_scores (
student TEXT,
score INT
);
| student | score |
|---|
| Ann | 95 |
| Bob | 88 |
| Cara | 95 |
| Dan | 88 |
| Eve | 72 |
| Finn | 88 |
Task
Rank students by score from highest to lowest. Return student, score, the RANK value as rnk, and the DENSE_RANK value as dense_rnk. Order the output by score descending, then student ascending.
Expected output
| student | score | rnk | dense_rnk |
|---|
| Ann | 95 | 1 | 1 |
| Cara | 95 | 1 | 1 |
| Bob | 88 | 3 | 2 |
| Dan | 88 | 3 | 2 |
| Finn | 88 | 3 | 2 |
| Eve | 72 | 6 | 3 |
Approach
Both functions share the same ORDER BY score DESC window, so they agree on which rows tie. They differ only in what happens after a tie: RANK skips the positions the tied rows consumed, while DENSE_RANK keeps numbering consecutively. Computing both in one SELECT makes the gap-vs-no-gap behavior visible.
Query
SELECT
student,
score,
RANK() OVER (ORDER BY score DESC) AS rnk,
DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rnk
FROM exam_scores
ORDER BY score DESC, student;
Walkthrough
- The two
95 rows (Ann, Cara) tie for the top: both get rnk = 1 and dense_rnk = 1.
- The three
88 rows come next. Two rows already occupied positions 1 and 2, so RANK resumes at 3 for all three. DENSE_RANK ignores the count and simply moves to the next distinct value, 2.
- The lone
72 (Eve) follows. RANK has now passed five rows, so it is 6; DENSE_RANK gives it 3, the third distinct score.
- The outer
ORDER BY score DESC, student is independent of the window ordering and just fixes the display sequence and the alphabetical tie-break within equal scores.
Complexity & notes
- Both windows use the identical
ORDER BY, so the planner sorts once and computes both functions in a single pass — cost is one O(n log n) sort.
rank and row_number are reserved words in some tools; aliasing to rnk / dense_rnk avoids the need to quote them.
- NULL scores would sort together (all NULLs are “equal” for ranking) and land last under
DESC with NULLS LAST semantics being dialect-dependent — add an explicit NULLS LAST if the data can contain them.
- Use
RANK when a skipped position is meaningful (e.g. “no 2nd place awarded because two tied for 1st”); use DENSE_RANK when you need a compact 1..k labeling of distinct tiers.