Revenue by category
Finance wants one number per category: how much money delivered orders brought in.
Revenue for a line item is quantity * unit_price. Only count orders whose status is 'delivered'.
Cancelled, returned and still-shipping orders don’t count yet.
Return one row per category with:
| column | type | meaning |
|---|---|---|
category |
text | the product category |
revenue |
decimal | total delivered revenue, rounded to 2 decimals |
Row order doesn’t matter.
Tables: orders, order_items, products.
bitshop
A small online shop for developers. Synthetic data, generated by scripts/gen-datasets.mjs.
Click a table to preview its first 10 rows.
customers 200 rows
one row per customer. About 6% have no email. About 15% never placed an order.
products 40 rows
40 products in 5 categories.
orders 1,000 rows
1,000 orders, 2024–2025. Status is delivered, shipped, cancelled or returned.
order_items 2,423 rows
line items. unit_price can be below the list price (discounts).
price_changes 98 rows
list-price history per product (one row per change, effective from changed_at).
Hints get more specific as you go. Reveal one, try again, then come back.
Revenue lives on order_items, the status lives on orders, and the category lives on products.
You need all three tables in one query.
Join order_items to orders on order_id and to products on product_id, then filter
WHERE o.status = 'delivered' before you group.
GROUP BY p.category and ROUND(SUM(oi.quantity * oi.unit_price), 2).
Try one more hint first? The solution is always here, but working it out is what makes it stick.
Approach
Join the line items to their order (for the status) and their product (for the category), keep only delivered orders, then group.
SELECT p.category,
ROUND(SUM(oi.quantity * oi.unit_price), 2) AS revenue
FROM order_items oi
JOIN orders o ON o.order_id = oi.order_id
JOIN products p ON p.product_id = oi.product_id
WHERE o.status = 'delivered'
GROUP BY p.category;
Why it works
WHERE runs before GROUP BY, so undelivered line items never reach the sums. Use unit_price from
order_items, not price from products: the line item records what the customer actually paid,
discount included.
Under the hood
DuckDB builds a hash table keyed by category and updates one running sum per key as rows stream
past. Five categories means five entries, however many line items there are. That’s the whole cost
of a GROUP BY with few groups: one pass and a tiny hash map.
Per-problem discussion threads are coming. Until then, questions and better solutions are welcome on LinkedIn or by email at hello@builtfrombits.com.
Press ⌘ ↵ or Run to check the visible tests. Submit runs the hidden ones too. Everything runs in your browser.