Code Lab is in development. New lessons and improvements are on the way. Progress can be deleted.
Loading…
Code Lab is in development. New lessons and improvements are on the way. Progress can be deleted.
Loading…
Code Lab is in development. New lessons and improvements are on the way. Progress can be deleted.
SQL / POSTGRESQL / Learn / LESSON 07
With equal values ROW_NUMBER still hands out different numbers. RANK and DENSE_RANK give equal values the SAME rank. They differ after a tie: RANK skips (1, 1, 3), DENSE_RANK does not (1, 1, 2). Use RANK for "competition" ranking, DENSE_RANK for gap-free levels ("the second highest price").
Careful: equality depends on the columns in OVER (ORDER BY ...). If you add a unique second key (id) inside OVER itself, every row becomes distinct and RANK behaves like ROW_NUMBER — equal values are no longer equal. Put a tie-breaker only in the final ORDER BY of the query for a predictable display, and keep it out of OVER when you want ties.
With PARTITION BY, ranking runs separately in each group (for example per category). A NULL key forms its own group — filter it out if you do not want it.
Common mistakes: id inside OVER for RANK/DENSE_RANK; mixing up the two functions; filtering the rank in the same SELECT; ranking in the wrong direction (ASC/DESC).
Small example:
SELECT name, price, RANK() OVER (ORDER BY price DESC) AS price_rank FROM products;
Working tables for this task. Grading also uses other datasets with the same schema.
| id | name | category | price |
|---|---|---|---|
| 1 | Atlas | books | 30 |
| 2 | Novel | books | 15 |
| 3 | Comic | books | 14.99 |
| 4 | Lamp | home | 29.99 |
| 5 | Vase | home | 30 |
| 6 | Rug | home | 45 |
| 7 | Hammer | tools | 15 |
| 8 | Drill | tools | 60 |
| 9 | Tape | tools | 15 |
| 10 | Candle | NULL | 8.5 |
| 11 | Gift Card | NULL | 25 |
| id | name | city |
|---|---|---|
| 1 | Mira | Sofia |
| 2 | Niko | Varna |
| 3 | Iva | Sofia |
| 4 | Alex | NULL |
| 5 | Boris | Plovdiv |
| 6 | Lena | Varna |
| 7 | Petya | Sofia |
| id | customer_id | total | order_date |
|---|---|---|---|
| 1 | 1 | 120 | 2024-01-05 |
| 2 | 1 | 35.5 | 2024-01-20 |
| 3 | 2 | 40 | 2024-01-20 |
| 4 | 3 | 99.99 | 2024-01-31 |
| 5 | NULL | 60 | 2024-02-01 |
| 6 | 1 | 40 | 2024-02-01 |
| 7 | 2 | 75.25 | 2024-02-14 |
| 8 | 3 | 100 | 2024-02-15 |
| 9 | 5 | 22 | 2024-02-15 |
| 10 | 1 | 39.99 | 2024-02-29 |
| 11 | 2 | 55 | 2024-03-01 |
| 12 | 3 | 18 | 2024-03-10 |
| 13 | 1 | 130 | 2024-03-15 |
| 14 | 2 | 40 | 2024-03-15 |
| 15 | 2 | 12.5 | 2024-03-31 |
| 16 | 3 | 47 | 2024-04-01 |
Expected columns: id, total, rnk, dense_rnk · row order is checked
Expected result (displayed data):
Loading…With equal values ROW_NUMBER still hands out different numbers. RANK and DENSE_RANK give equal values the SAME rank. They differ after a tie: RANK skips (1, 1, 3), DENSE_RANK does not (1, 1, 2). Use RANK for "competition" ranking, DENSE_RANK for gap-free levels ("the second highest price"). Careful: equality depends on the columns in OVER (ORDER BY ...). If you add a unique second key (id) inside OVER itself, every row becomes distinct and RANK behaves like ROW_NUMBER — equal values are no longer equal. Put a tie-breaker only in the final ORDER BY of the query for a predictable display, and keep it out of OVER when you want ties. With PARTITION BY, ranking runs separately in each group (for example per category). A NULL key forms its own group — filter it out if you do not want it. Common mistakes: id inside OVER for RANK/DENSE_RANK; mixing up the two functions; filtering the rank in the same SELECT; ranking in the wrong direction (ASC/DESC).
QUERY STANDBY
›