{"id":"data2-sql-top-customers-net-revenue","audience":"analysts","category":"data","track":"data","technique":"constraints","level":"intermediate","title":"Write SQL for top customers by net revenue","free":false,"task":"A sales leader asked: who are our 5 biggest customers by net revenue in 2025? Ask for one Postgres query in a single code block, with at most 40 words of writing outside it. Net revenue means completed orders only, minus refunds, shown in dollars.","materialLabel":"Schema and notes (attached to your prompt automatically)","material":"Tables (Postgres), Cedar Supply data warehouse\n\ncustomers(customer_id, name, region)\norders(order_id, customer_id, order_date, status, total_cents)\n  status values: 'completed', 'cancelled', 'pending'\nrefunds(refund_id, order_id, refund_cents, refunded_at)\n\nNotes from the data team:\n- All money columns are in cents (integers).\n- An order can have zero, one or several refund rows.\n- order_date is a date column; the fiscal year equals the calendar year.\n- A previous query joined orders to refunds directly and then summed total_cents, which made big orders with 3 refunds count three times.\n- Last year's report ranked by order count by mistake; the ask this time is revenue.\n- Region is not needed in the output.\n- Output columns wanted by the sales leader: customer name and net revenue.","rules":[{"type":"codeBlocks","count":1},{"type":"maxProseWords","max":40}],"mistakes":[{"title":"Joining refunds straight to orders","why":"The AI joins one-to-many and sums order totals again for every refund row.","fix":"Add: \"Sum refunds per order first, then LEFT JOIN so nothing is counted twice.\"","checks":["r0"]},{"title":"Forgetting the status and year","why":"Without being told, the AI counts every order, including cancelled and pending ones.","fix":"Add: \"Completed orders only, order_date in 2025.\"","checks":["r1"]},{"title":"Leaving money in cents","why":"The AI returns raw cents and a long unranked list.","fix":"Add: \"Divide cents by 100, order by net revenue descending, LIMIT 5.\"","checks":["r2"]},{"title":"Leaving the format to chance","why":"The AI picks its own length and layout unless told the exact shape.","fix":"Add: \"Put the code in exactly 1 code block. Keep the writing outside the code under 40 words.\"","checks":["codeBlocks","maxProseWords"]}],"promptHints":[{"label":"Names the filters","patterns":["completed","2025","status"]},{"label":"Warns about several refunds per order","patterns":["several refunds","per order","double count","twice","subquery","cte"]},{"label":"Says the units","patterns":["cents","/ ?100","dollars"]},{"label":"Asks for ranked top 5","patterns":["limit 5","top 5","descending","highest first"]},{"label":"Caps the writing","patterns":["\\d+\\s*(short )?(sentences|words)","code block"]}],"hint":"A correct-looking query can still double count. Tell the AI the grain problem (several refunds per order), the filters and the units.","mission":"Get the top 5 customers by 2025 net revenue.","musts":["One Postgres query","Completed 2025 orders only","Refunds not double counted"],"rubricLabels":["The query keeps only orders with status 'completed' and order_date in 2025 (for example >= '2025-01-01' and < '2026-01-01', or the year 2025 extracted)","Refunds are summed per order (in a subquery, CTE or pre-aggregated join) or otherwise handled so orders with no refunds still count and an order with several refunds is not counted more than once","It groups by customer, divides cents by 100 to give dollars, orders by net revenue descending and limits to 5 rows, returning the customer name and net revenue"]}