Top N per Group With Ties
SQLRanking with tiesSeniorStaffUpdated
Return the top 2 sales reps in each region by total amount. If two reps tie, both should appear and the next rep should be skipped (classic 1, 2, 2, 4 ranking).
Schema
CREATE TABLE sales (
id BIGINT PRIMARY KEY,
region TEXT NOT NULL,
rep_name TEXT NOT NULL,
amount NUMERIC NOT NULL,
sold_at DATE NOT NULL
);Drill This: Your Query First
The Correct Query
Option (b)
SELECT region, rep_name, total
FROM (
SELECT region, rep_name, SUM(amount) AS total,
RANK() OVER (PARTITION BY region ORDER BY SUM(amount) DESC) AS rk
FROM sales
GROUP BY region, rep_name
) t
WHERE rk <= 2;RANK() produces 1, 2, 2, 4: exactly the 'include ties, skip the next slot' semantics asked for. ROW_NUMBER (a) breaks ties arbitrarily and would drop a tied rep. DENSE_RANK (c) would let a third rep through because it produces 1, 2, 2, 3. LIMIT 2 (d) returns 2 rows globally, not 2 per region.
Going Deeper
ROW_NUMBER, RANK, and DENSE_RANK only differ on ties. Pick by what the product says about ties: arbitrary tiebreak (ROW_NUMBER), include-and-skip (RANK), include-and-continue (DENSE_RANK).
The Options That Fail
The explanation above says why each of these breaks.
Option (a)
SELECT region, rep_name, total
FROM (
SELECT region, rep_name, SUM(amount) AS total,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY SUM(amount) DESC) AS rn
FROM sales
GROUP BY region, rep_name
) t
WHERE rn <= 2;Option (c)
SELECT region, rep_name, total
FROM (
SELECT region, rep_name, SUM(amount) AS total,
DENSE_RANK() OVER (PARTITION BY region ORDER BY SUM(amount) DESC) AS dr
FROM sales
GROUP BY region, rep_name
) t
WHERE dr <= 2;Option (d)
SELECT region, rep_name, SUM(amount) AS total
FROM sales
GROUP BY region, rep_name
ORDER BY region, total DESC
LIMIT 2;