SolveWithSQL

SQL Intermediate Challenge 3: Second-Highest Product per Category

Difficulty: Intermediate
Estimated time: 30–40 minutes
SQL concepts: correlated subqueries, ordering, tie awareness, optional window functions
Goal: Return the second-highest priced product per category, reliably and deterministically.

The Challenge

The product manager asks:

“For each category, which product is the second most expensive?”

This sounds like a small change from “top product per category,”
but it introduces a much harder reasoning problem.

Why?

  • You can’t use MAX() directly
  • You must think carefully about ordering and offsets
  • You must decide how to handle ties

Database Table

products

column_nametypedescription
product_idINTEGERUnique product ID
nameTEXTProduct name
categoryTEXTProduct category
priceDECIMALProduct price

Sample Data (includes ties)

product_idnamecategoryprice
201Wireless MouseAccessories24.99
202Mechanical KeyboardAccessories89.00
203Pro KeyboardAccessories89.00
204USB-C HubAccessories34.50
205MonitorDisplays229.99
20634-inch MonitorDisplays399.00
207Desk LampWorkspace49.99
208Chair MatWorkspace79.99

Your Task

Return one row per category with:

  • category
  • name
  • price

Where the product is the second-highest priced in that category.

Tie rule (required)

If multiple products share the same price:

  • Order by price DESC
  • Then by product_id ASC
  • The second row after sorting is the answer

Sort final results by:

  • category (A–Z)

Expected Output (based on sample data)

Accessories sorted by price DESC:

  1. 202 → 89.00
  2. 203 → 89.00 ← second
  3. 204 → 34.50
  4. 201 → 24.99

Displays:

  1. 206 → 399.00
  2. 205 → 229.99

Workspace:

  1. 208 → 79.99
  2. 207 → 49.99
categorynameprice
AccessoriesPro Keyboard89.00
DisplaysMonitor229.99
WorkspaceDesk Lamp49.99

Constraints

  • Do not use SELECT *
  • Must return exactly one row per category
  • Must follow the tie rule
  • Must work reliably even with equal prices

Hint (Optional)

This problem is much easier if you:

  1. Sort products within each category
  2. Assign an order
  3. Pick the row ranked 2

You can do this with:

  • a correlated subquery (SQLite-safe)
  • or a window function (cleaner, if supported)

SELECT
p.category,
p.name,
p.price
FROM products p
WHERE p.product_id = (
SELECT p2.product_id
FROM products p2
WHERE p2.category = p.category
ORDER BY p2.price DESC, p2.product_id ASC
LIMIT 1 OFFSET 1
)
ORDER BY p.category ASC;

SELECT
category,
name,
price
FROM (
SELECT
category,
name,
price,
ROW_NUMBER() OVER (
PARTITION BY category
ORDER BY price DESC, product_id ASC
) AS rn
FROM products
) ranked
WHERE rn = 2
ORDER BY category ASC;

Why this works (Solution A)

  • The inner query:
    • sorts products per category
    • skips the first (highest)
    • selects the second
  • The outer query returns only the matching row

Explanation

  • ROW_NUMBER() assigns an order within each category
  • The second-highest product always receives rn = 2
  • Tie-breaking is handled explicitly
  • Output is stable and predictable

This pattern generalizes to:

  • 3rd-highest
  • top-N per group
  • percentile-based analysis

🔑 Key Insight

Finding “second best” is harder than “best.”

Once you can solve this, you’re genuinely operating at an intermediate level.

Common Mistakes

  • Using MAX(price) twice (doesn’t work)
  • Returning multiple rows per category
  • Forgetting deterministic tie ordering
  • Using LIMIT 2 globally instead of per category

Optional Extension (Mini Bonus)

  1. Return the third-highest product per category
  2. Return categories with at least 3 products only
  3. Show the price difference between highest and second-highest

🔗 Next Intermediate Challenge

Intermediate Challenge #4: Products Above Category Median (why median is trickier than average)

🔗 View reference solution on GitHub
(After you’ve tried the challenge)

Want more practical SQL challenges?
Subscribe to the Solve With SQL newsletter and get new problems delivered to your inbox.