Intermediate Case 21 of 50

Case 021: The Leaderboard

customersorderspayments

Welcome to Intermediate. The fundamentals are locked in. Now it's time to think more like an analyst.

Your first challenge: window functions. The manager has a question for the upcoming sales meeting:

"Could you rank every customer by how much they've spent? I want to see who's #1."

The data Alex is looking at

customers
idnamecountrysignup_date
1SamGermany2026-01-02
2NinaCanada2026-01-05
3DavidGermany2026-01-10
4MayaJapan2026-01-15
5DanielCanada2026-01-20
… 5 more rows
orders
idcustomer_idorder_date
10112026-01-05
10222026-01-08
10332026-01-12
10412026-01-20
10542026-02-01
… 23 more rows
payments
idorder_idamount
6011016200
6021023500
6031033600
60410460000
60510512000
… 23 more rows
TASK

Help Alex answer the manager:

“Rank every customer by total spending, highest first, showing both their total spent and their rank.”
SQL editor Ctrl+Enter to run
Write a query above and hit Run to see what comes back.
No hints requested yet — click "Get a hint" when you're stuck.
Alex's final query — this is for reference. Copy it into your own thinking, not into the editor above.
SELECT customers.name, SUM(payments.amount) AS total_spent, RANK() OVER (ORDER BY SUM(payments.amount) DESC) AS spending_rank FROM customers JOIN orders ON customers.id = orders.customer_id JOIN payments ON payments.order_id = orders.id GROUP BY customers.name;
What that query returns
Case Debrief RANK() window function
  • RANK() OVER (ORDER BY ...) assigns a rank to every row based on a value.
  • DESC inside that ORDER BY is what makes rank #1 the biggest spender — RANK() OVER (ORDER BY SUM(payments.amount) DESC) ranks highest-to-lowest. Drop the DESC (or write ASC) and rank #1 would go to the smallest spender instead.
  • It can rank an aggregate like SUM() in the very same query that computes it — no extra pass needed.
  • Quick tip: this query spells out customers, orders, and payments in full every time. Once you're joining several tables, aliasing them — customers AS c JOIN orders AS o JOIN payments AS p — lets you write c.name, o.customer_id, p.amount instead, which gets easier to read fast as queries grow.

Case closed.

Ready for the next one?

Next: Case 022 — The Latest Order →